added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / ObjectStore / src / list.cpp
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2015 Realm Inc.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 ////////////////////////////////////////////////////////////////////////////
18
19 #include "list.hpp"
20
21 #include "impl/list_notifier.hpp"
22 #include "impl/primitive_list_notifier.hpp"
23 #include "impl/realm_coordinator.hpp"
24 #include "object_schema.hpp"
25 #include "object_store.hpp"
26 #include "results.hpp"
27 #include "schema.hpp"
28 #include "shared_realm.hpp"
29
30 #include <realm/link_view.hpp>
31
32 namespace realm {
33 using namespace realm::_impl;
34
35 List::List() noexcept = default;
36 List::~List() = default;
37
38 List::List(const List&) = default;
39 List& List::operator=(const List&) = default;
40 List::List(List&&) = default;
41 List& List::operator=(List&&) = default;
42
43 List::List(std::shared_ptr<Realm> r, Table& parent_table, size_t col, size_t row)
44 : m_realm(std::move(r))
45 {
46     auto type = parent_table.get_column_type(col);
47     REALM_ASSERT(type == type_LinkList || type == type_Table);
48     if (type == type_LinkList) {
49         m_link_view = parent_table.get_linklist(col, row);
50         m_table.reset(&m_link_view->get_target_table());
51     }
52     else {
53         m_table = parent_table.get_subtable(col, row);
54     }
55 }
56
57 List::List(std::shared_ptr<Realm> r, LinkViewRef l) noexcept
58 : m_realm(std::move(r))
59 , m_link_view(std::move(l))
60 {
61     m_table.reset(&m_link_view->get_target_table());
62 }
63
64 List::List(std::shared_ptr<Realm> r, TableRef t) noexcept
65 : m_realm(std::move(r))
66 , m_table(std::move(t))
67 {
68 }
69
70 static StringData object_name(Table const& table)
71 {
72     return ObjectStore::object_type_for_table_name(table.get_name());
73 }
74
75 ObjectSchema const& List::get_object_schema() const
76 {
77     verify_attached();
78     REALM_ASSERT(m_link_view);
79
80     if (!m_object_schema) {
81         REALM_ASSERT(get_type() == PropertyType::Object);
82         auto object_type = object_name(m_link_view->get_target_table());
83         auto it = m_realm->schema().find(object_type);
84         REALM_ASSERT(it != m_realm->schema().end());
85         m_object_schema = &*it;
86     }
87     return *m_object_schema;
88 }
89
90 Query List::get_query() const
91 {
92     verify_attached();
93     return m_link_view ? m_table->where(m_link_view) : m_table->where();
94 }
95
96 size_t List::get_origin_row_index() const
97 {
98     verify_attached();
99     return m_link_view ? m_link_view->get_origin_row_index() : m_table->get_parent_row_index();
100 }
101
102 void List::verify_valid_row(size_t row_ndx, bool insertion) const
103 {
104     size_t s = size();
105     if (row_ndx > s || (!insertion && row_ndx == s)) {
106         throw OutOfBoundsIndexException{row_ndx, s + insertion};
107     }
108 }
109
110 void List::validate(RowExpr row) const
111 {
112     if (!row.is_attached())
113         throw std::invalid_argument("Object has been deleted or invalidated");
114     if (row.get_table() != &m_link_view->get_target_table())
115         throw std::invalid_argument(util::format("Object of type (%1) does not match List type (%2)",
116                                                  object_name(*row.get_table()),
117                                                  object_name(m_link_view->get_target_table())));
118 }
119
120 bool List::is_valid() const
121 {
122     if (!m_realm)
123         return false;
124     m_realm->verify_thread();
125     if (m_link_view)
126         return m_link_view->is_attached();
127     return m_table && m_table->is_attached();
128 }
129
130 void List::verify_attached() const
131 {
132     if (!is_valid()) {
133         throw InvalidatedException();
134     }
135 }
136
137 void List::verify_in_transaction() const
138 {
139     verify_attached();
140     m_realm->verify_in_write();
141 }
142
143 size_t List::size() const
144 {
145     verify_attached();
146     return m_link_view ? m_link_view->size() : m_table->size();
147 }
148
149 size_t List::to_table_ndx(size_t row) const noexcept
150 {
151     return m_link_view ? m_link_view->get(row).get_index() : row;
152 }
153
154 PropertyType List::get_type() const
155 {
156     verify_attached();
157     return m_link_view ? PropertyType::Object
158                        : ObjectSchema::from_core_type(*m_table->get_descriptor(), 0);
159 }
160
161 namespace {
162 template<typename T>
163 auto get(Table& table, size_t row)
164 {
165     return table.get<T>(0, row);
166 }
167
168 template<>
169 auto get<RowExpr>(Table& table, size_t row)
170 {
171     return table.get(row);
172 }
173 }
174
175 template<typename T>
176 T List::get(size_t row_ndx) const
177 {
178     verify_valid_row(row_ndx);
179     return realm::get<T>(*m_table, to_table_ndx(row_ndx));
180 }
181
182 template RowExpr List::get(size_t) const;
183
184 template<typename T>
185 size_t List::find(T const& value) const
186 {
187     verify_attached();
188     return m_table->find_first(0, value);
189 }
190
191 template<>
192 size_t List::find(RowExpr const& row) const
193 {
194     verify_attached();
195     if (!row.is_attached())
196         return not_found;
197     validate(row);
198
199     return m_link_view ? m_link_view->find(row.get_index()) : row.get_index();
200 }
201
202 size_t List::find(Query&& q) const
203 {
204     verify_attached();
205     if (m_link_view) {
206         size_t index = get_query().and_query(std::move(q)).find();
207         return index == not_found ? index : m_link_view->find(index);
208     }
209     return q.find();
210 }
211
212 template<typename T>
213 void List::add(T value)
214 {
215     verify_in_transaction();
216     m_table->set(0, m_table->add_empty_row(), value);
217 }
218
219 template<>
220 void List::add(size_t target_row_ndx)
221 {
222     verify_in_transaction();
223     m_link_view->add(target_row_ndx);
224 }
225
226 template<>
227 void List::add(RowExpr row)
228 {
229     validate(row);
230     add(row.get_index());
231 }
232
233 template<>
234 void List::add(int value)
235 {
236     verify_in_transaction();
237     if (m_link_view)
238         add(static_cast<size_t>(value));
239     else
240         add(static_cast<int64_t>(value));
241 }
242
243 template<typename T>
244 void List::insert(size_t row_ndx, T value)
245 {
246     verify_in_transaction();
247     verify_valid_row(row_ndx, true);
248     m_table->insert_empty_row(row_ndx);
249     m_table->set(0, row_ndx, value);
250 }
251
252 template<>
253 void List::insert(size_t row_ndx, size_t target_row_ndx)
254 {
255     verify_in_transaction();
256     verify_valid_row(row_ndx, true);
257     m_link_view->insert(row_ndx, target_row_ndx);
258 }
259
260 template<>
261 void List::insert(size_t row_ndx, RowExpr row)
262 {
263     validate(row);
264     insert(row_ndx, row.get_index());
265 }
266
267 void List::move(size_t source_ndx, size_t dest_ndx)
268 {
269     verify_in_transaction();
270     verify_valid_row(source_ndx);
271     verify_valid_row(dest_ndx); // Can't be one past end due to removing one earlier
272     if (source_ndx == dest_ndx)
273         return;
274
275     if (m_link_view)
276         m_link_view->move(source_ndx, dest_ndx);
277     else
278         m_table->move_row(source_ndx, dest_ndx);
279 }
280
281 void List::remove(size_t row_ndx)
282 {
283     verify_in_transaction();
284     verify_valid_row(row_ndx);
285     if (m_link_view)
286         m_link_view->remove(row_ndx);
287     else
288         m_table->remove(row_ndx);
289 }
290
291 void List::remove_all()
292 {
293     verify_in_transaction();
294     if (m_link_view)
295         m_link_view->clear();
296     else
297         m_table->clear();
298 }
299
300 template<typename T>
301 void List::set(size_t row_ndx, T value)
302 {
303     verify_in_transaction();
304     verify_valid_row(row_ndx);
305     m_table->set(0, row_ndx, value);
306 }
307
308 template<>
309 void List::set(size_t row_ndx, size_t target_row_ndx)
310 {
311     verify_in_transaction();
312     verify_valid_row(row_ndx);
313     m_link_view->set(row_ndx, target_row_ndx);
314 }
315
316 template<>
317 void List::set(size_t row_ndx, RowExpr row)
318 {
319     validate(row);
320     set(row_ndx, row.get_index());
321 }
322
323 void List::swap(size_t ndx1, size_t ndx2)
324 {
325     verify_in_transaction();
326     verify_valid_row(ndx1);
327     verify_valid_row(ndx2);
328     if (m_link_view)
329         m_link_view->swap(ndx1, ndx2);
330     else
331         m_table->swap_rows(ndx1, ndx2);
332 }
333
334 void List::delete_at(size_t row_ndx)
335 {
336     verify_in_transaction();
337     verify_valid_row(row_ndx);
338     if (m_link_view)
339         m_link_view->remove_target_row(row_ndx);
340     else
341         m_table->remove(row_ndx);
342 }
343
344 void List::delete_all()
345 {
346     verify_in_transaction();
347     if (m_link_view)
348         m_link_view->remove_all_target_rows();
349     else
350         m_table->clear();
351 }
352
353 Results List::sort(SortDescriptor order) const
354 {
355     verify_attached();
356     if (m_link_view)
357         return Results(m_realm, m_link_view, util::none, std::move(order));
358
359     DescriptorOrdering new_order;
360     new_order.append_sort(std::move(order));
361     return Results(m_realm, get_query(), std::move(new_order));
362 }
363
364 Results List::sort(std::vector<std::pair<std::string, bool>> const& keypaths) const
365 {
366     return as_results().sort(keypaths);
367 }
368
369 Results List::filter(Query q) const
370 {
371     verify_attached();
372     if (m_link_view)
373         return Results(m_realm, m_link_view, get_query().and_query(std::move(q)));
374     return Results(m_realm, get_query().and_query(std::move(q)));
375 }
376
377 Results List::as_results() const
378 {
379     verify_attached();
380     return m_link_view ? Results(m_realm, m_link_view) : Results(m_realm, *m_table);
381 }
382
383 Results List::snapshot() const
384 {
385     return as_results().snapshot();
386 }
387
388 util::Optional<Mixed> List::max(size_t column)
389 {
390     return as_results().max(column);
391 }
392
393 util::Optional<Mixed> List::min(size_t column)
394 {
395     return as_results().min(column);
396 }
397
398 Mixed List::sum(size_t column)
399 {
400     // Results::sum() returns none only for Mode::Empty Results, so we can
401     // safely ignore that possibility here
402     return *as_results().sum(column);
403 }
404
405 util::Optional<double> List::average(size_t column)
406 {
407     return as_results().average(column);
408 }
409
410 // These definitions rely on that LinkViews and Tables are interned by core
411 bool List::operator==(List const& rgt) const noexcept
412 {
413     return m_link_view == rgt.m_link_view && m_table.get() == rgt.m_table.get();
414 }
415
416 NotificationToken List::add_notification_callback(CollectionChangeCallback cb) &
417 {
418     verify_attached();
419     // Adding a new callback to a notifier which had all of its callbacks
420     // removed does not properly reinitialize the notifier. Work around this by
421     // recreating it instead.
422     // FIXME: The notifier lifecycle here is dumb (when all callbacks are removed
423     // from a notifier a zombie is left sitting around uselessly) and should be
424     // cleaned up.
425     if (m_notifier && !m_notifier->have_callbacks())
426         m_notifier.reset();
427     if (!m_notifier) {
428         if (get_type() == PropertyType::Object)
429             m_notifier = std::static_pointer_cast<_impl::CollectionNotifier>(std::make_shared<ListNotifier>(m_link_view, m_realm));
430         else
431             m_notifier = std::static_pointer_cast<_impl::CollectionNotifier>(std::make_shared<PrimitiveListNotifier>(m_table, m_realm));
432         RealmCoordinator::register_notifier(m_notifier);
433     }
434     return {m_notifier, m_notifier->add_callback(std::move(cb))};
435 }
436
437 List::OutOfBoundsIndexException::OutOfBoundsIndexException(size_t r, size_t c)
438 : std::out_of_range(util::format("Requested index %1 greater than max %2", r, c - 1))
439 , requested(r), valid_count(c) {}
440
441 #define REALM_PRIMITIVE_LIST_TYPE(T) \
442     template T List::get<T>(size_t) const; \
443     template size_t List::find<T>(T const&) const; \
444     template void List::add<T>(T); \
445     template void List::insert<T>(size_t, T); \
446     template void List::set<T>(size_t, T);
447
448 REALM_PRIMITIVE_LIST_TYPE(bool)
449 REALM_PRIMITIVE_LIST_TYPE(int64_t)
450 REALM_PRIMITIVE_LIST_TYPE(float)
451 REALM_PRIMITIVE_LIST_TYPE(double)
452 REALM_PRIMITIVE_LIST_TYPE(StringData)
453 REALM_PRIMITIVE_LIST_TYPE(BinaryData)
454 REALM_PRIMITIVE_LIST_TYPE(Timestamp)
455 REALM_PRIMITIVE_LIST_TYPE(util::Optional<bool>)
456 REALM_PRIMITIVE_LIST_TYPE(util::Optional<int64_t>)
457 REALM_PRIMITIVE_LIST_TYPE(util::Optional<float>)
458 REALM_PRIMITIVE_LIST_TYPE(util::Optional<double>)
459
460 #undef REALM_PRIMITIVE_LIST_TYPE
461 } // namespace realm
462
463 namespace std {
464 size_t hash<realm::List>::operator()(realm::List const& list) const
465 {
466     return std::hash<void*>()(list.m_link_view ? list.m_link_view.get() : (void*)list.m_table.get());
467 }
468 }