1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2015 Realm Inc.
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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.
17 ////////////////////////////////////////////////////////////////////////////
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"
28 #include "shared_realm.hpp"
30 #include <realm/link_view.hpp>
33 using namespace realm::_impl;
35 List::List() noexcept = default;
36 List::~List() = default;
38 List::List(const List&) = default;
39 List& List::operator=(const List&) = default;
40 List::List(List&&) = default;
41 List& List::operator=(List&&) = default;
43 List::List(std::shared_ptr<Realm> r, Table& parent_table, size_t col, size_t row)
44 : m_realm(std::move(r))
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());
53 m_table = parent_table.get_subtable(col, row);
57 List::List(std::shared_ptr<Realm> r, LinkViewRef l) noexcept
58 : m_realm(std::move(r))
59 , m_link_view(std::move(l))
61 m_table.reset(&m_link_view->get_target_table());
64 List::List(std::shared_ptr<Realm> r, TableRef t) noexcept
65 : m_realm(std::move(r))
66 , m_table(std::move(t))
70 static StringData object_name(Table const& table)
72 return ObjectStore::object_type_for_table_name(table.get_name());
75 ObjectSchema const& List::get_object_schema() const
78 REALM_ASSERT(m_link_view);
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;
87 return *m_object_schema;
90 Query List::get_query() const
93 return m_link_view ? m_table->where(m_link_view) : m_table->where();
96 size_t List::get_origin_row_index() const
99 return m_link_view ? m_link_view->get_origin_row_index() : m_table->get_parent_row_index();
102 void List::verify_valid_row(size_t row_ndx, bool insertion) const
105 if (row_ndx > s || (!insertion && row_ndx == s)) {
106 throw OutOfBoundsIndexException{row_ndx, s + insertion};
110 void List::validate(RowExpr row) const
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())));
120 bool List::is_valid() const
124 m_realm->verify_thread();
126 return m_link_view->is_attached();
127 return m_table && m_table->is_attached();
130 void List::verify_attached() const
133 throw InvalidatedException();
137 void List::verify_in_transaction() const
140 m_realm->verify_in_write();
143 size_t List::size() const
146 return m_link_view ? m_link_view->size() : m_table->size();
149 size_t List::to_table_ndx(size_t row) const noexcept
151 return m_link_view ? m_link_view->get(row).get_index() : row;
154 PropertyType List::get_type() const
157 return m_link_view ? PropertyType::Object
158 : ObjectSchema::from_core_type(*m_table->get_descriptor(), 0);
163 auto get(Table& table, size_t row)
165 return table.get<T>(0, row);
169 auto get<RowExpr>(Table& table, size_t row)
171 return table.get(row);
176 T List::get(size_t row_ndx) const
178 verify_valid_row(row_ndx);
179 return realm::get<T>(*m_table, to_table_ndx(row_ndx));
182 template RowExpr List::get(size_t) const;
185 size_t List::find(T const& value) const
188 return m_table->find_first(0, value);
192 size_t List::find(RowExpr const& row) const
195 if (!row.is_attached())
199 return m_link_view ? m_link_view->find(row.get_index()) : row.get_index();
202 size_t List::find(Query&& q) const
206 size_t index = get_query().and_query(std::move(q)).find();
207 return index == not_found ? index : m_link_view->find(index);
213 void List::add(T value)
215 verify_in_transaction();
216 m_table->set(0, m_table->add_empty_row(), value);
220 void List::add(size_t target_row_ndx)
222 verify_in_transaction();
223 m_link_view->add(target_row_ndx);
227 void List::add(RowExpr row)
230 add(row.get_index());
234 void List::add(int value)
236 verify_in_transaction();
238 add(static_cast<size_t>(value));
240 add(static_cast<int64_t>(value));
244 void List::insert(size_t row_ndx, T value)
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);
253 void List::insert(size_t row_ndx, size_t target_row_ndx)
255 verify_in_transaction();
256 verify_valid_row(row_ndx, true);
257 m_link_view->insert(row_ndx, target_row_ndx);
261 void List::insert(size_t row_ndx, RowExpr row)
264 insert(row_ndx, row.get_index());
267 void List::move(size_t source_ndx, size_t dest_ndx)
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)
276 m_link_view->move(source_ndx, dest_ndx);
278 m_table->move_row(source_ndx, dest_ndx);
281 void List::remove(size_t row_ndx)
283 verify_in_transaction();
284 verify_valid_row(row_ndx);
286 m_link_view->remove(row_ndx);
288 m_table->remove(row_ndx);
291 void List::remove_all()
293 verify_in_transaction();
295 m_link_view->clear();
301 void List::set(size_t row_ndx, T value)
303 verify_in_transaction();
304 verify_valid_row(row_ndx);
305 m_table->set(0, row_ndx, value);
309 void List::set(size_t row_ndx, size_t target_row_ndx)
311 verify_in_transaction();
312 verify_valid_row(row_ndx);
313 m_link_view->set(row_ndx, target_row_ndx);
317 void List::set(size_t row_ndx, RowExpr row)
320 set(row_ndx, row.get_index());
323 void List::swap(size_t ndx1, size_t ndx2)
325 verify_in_transaction();
326 verify_valid_row(ndx1);
327 verify_valid_row(ndx2);
329 m_link_view->swap(ndx1, ndx2);
331 m_table->swap_rows(ndx1, ndx2);
334 void List::delete_at(size_t row_ndx)
336 verify_in_transaction();
337 verify_valid_row(row_ndx);
339 m_link_view->remove_target_row(row_ndx);
341 m_table->remove(row_ndx);
344 void List::delete_all()
346 verify_in_transaction();
348 m_link_view->remove_all_target_rows();
353 Results List::sort(SortDescriptor order) const
357 return Results(m_realm, m_link_view, util::none, std::move(order));
359 DescriptorOrdering new_order;
360 new_order.append_sort(std::move(order));
361 return Results(m_realm, get_query(), std::move(new_order));
364 Results List::sort(std::vector<std::pair<std::string, bool>> const& keypaths) const
366 return as_results().sort(keypaths);
369 Results List::filter(Query q) const
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)));
377 Results List::as_results() const
380 return m_link_view ? Results(m_realm, m_link_view) : Results(m_realm, *m_table);
383 Results List::snapshot() const
385 return as_results().snapshot();
388 util::Optional<Mixed> List::max(size_t column)
390 return as_results().max(column);
393 util::Optional<Mixed> List::min(size_t column)
395 return as_results().min(column);
398 Mixed List::sum(size_t column)
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);
405 util::Optional<double> List::average(size_t column)
407 return as_results().average(column);
410 // These definitions rely on that LinkViews and Tables are interned by core
411 bool List::operator==(List const& rgt) const noexcept
413 return m_link_view == rgt.m_link_view && m_table.get() == rgt.m_table.get();
416 NotificationToken List::add_notification_callback(CollectionChangeCallback cb) &
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
425 if (m_notifier && !m_notifier->have_callbacks())
428 if (get_type() == PropertyType::Object)
429 m_notifier = std::static_pointer_cast<_impl::CollectionNotifier>(std::make_shared<ListNotifier>(m_link_view, m_realm));
431 m_notifier = std::static_pointer_cast<_impl::CollectionNotifier>(std::make_shared<PrimitiveListNotifier>(m_table, m_realm));
432 RealmCoordinator::register_notifier(m_notifier);
434 return {m_notifier, m_notifier->add_callback(std::move(cb))};
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) {}
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);
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>)
460 #undef REALM_PRIMITIVE_LIST_TYPE
464 size_t hash<realm::List>::operator()(realm::List const& list) const
466 return std::hash<void*>()(list.m_link_view ? list.m_link_view.get() : (void*)list.m_table.get());