added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / link_view.hpp
1 /*************************************************************************
2  *
3  * Copyright 2016 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 #ifndef REALM_LINK_VIEW_HPP
20 #define REALM_LINK_VIEW_HPP
21
22 #include <realm/column.hpp>
23 #include <realm/column_linklist.hpp>
24 #include <realm/link_view_fwd.hpp>
25 #include <realm/table.hpp>
26
27 namespace realm {
28
29 class LinkListColumn;
30
31 namespace _impl {
32 class LinkListFriend;
33 class TransactLogConvenientEncoder;
34 }
35
36
37 /// The effect of calling most of the link list functions on a detached accessor
38 /// is unspecified and may lead to general corruption, or even a crash. The
39 /// exceptions are is_attached() and the destructor.
40 ///
41 /// FIXME: Rename this class to `LinkList`.
42 class LinkView : public RowIndexes, public std::enable_shared_from_this<LinkView> {
43 public:
44     ~LinkView() noexcept;
45     bool is_attached() const noexcept;
46
47     /// This method will return true if the LinkView is detached (no assert).
48     bool is_empty() const noexcept;
49
50     /// This method will return 0 if the LinkView is detached (no assert).
51     size_t size() const noexcept override;
52
53     bool operator==(const LinkView&) const noexcept;
54     bool operator!=(const LinkView&) const noexcept;
55
56     // Getting links
57     Table::ConstRowExpr operator[](size_t link_ndx) const noexcept;
58     Table::RowExpr operator[](size_t link_ndx) noexcept;
59     Table::ConstRowExpr get(size_t link_ndx) const noexcept;
60     Table::RowExpr get(size_t link_ndx) noexcept;
61
62     // Modifiers
63     void add(size_t target_row_ndx);
64     void insert(size_t link_ndx, size_t target_row_ndx);
65     void set(size_t link_ndx, size_t target_row_ndx);
66     /// Move the link at \a from_ndx such that it ends up at \a to_ndx. Other
67     /// links are shifted as necessary in such a way that their order is
68     /// preserved.
69     ///
70     /// Note that \a to_ndx is the desired final index of the moved link,
71     /// therefore, `move(1,1)` is a no-op, while `move(1,2)` moves the link at
72     /// index 1 by one position, such that it ends up at index 2. A side-effect
73     /// of that, is that the link, that was originally at index 2, is moved to
74     /// index 1.
75     void move(size_t from_ndx, size_t to_ndx);
76     void swap(size_t link1_ndx, size_t link2_ndx);
77     void remove(size_t link_ndx);
78     void clear();
79
80     void sort(size_t column, bool ascending = true);
81     void sort(SortDescriptor&& order);
82
83     TableView get_sorted_view(size_t column_index, bool ascending = true) const;
84     TableView get_sorted_view(SortDescriptor order) const;
85
86     /// Remove the target row of the specified link from the target table. This
87     /// also removes the specified link from this link list, and any other link
88     /// pointing to that row. This is merely a shorthand for
89     /// `get_target_table.move_last_over(get(link_ndx))`.
90     void remove_target_row(size_t link_ndx);
91
92     /// Remove all target rows pointed to by links in this link list, and clear
93     /// this link list.
94     void remove_all_target_rows();
95
96     /// Search this list for a link to the specified target table row (specified
97     /// by its index in the target table). If found, the index of the link to
98     /// that row within this list is returned, otherwise `realm::not_found` is
99     /// returned.
100     size_t find(size_t target_row_ndx, size_t start = 0) const noexcept;
101
102     const ColumnBase& get_column_base(size_t index)
103         const override; // FIXME: `ColumnBase` is not part of the public API, so this function must be made private.
104     const Table& get_origin_table() const noexcept;
105     Table& get_origin_table() noexcept;
106
107     size_t get_origin_row_index() const noexcept;
108
109     const Table& get_target_table() const noexcept;
110     Table& get_target_table() noexcept;
111
112     // No-op because LinkViews are always kept in sync.
113     uint_fast64_t sync_if_needed() const override;
114     bool is_in_sync() const override
115     {
116         return true;
117     }
118
119 private:
120     struct ctor_cookie {
121     };
122
123     TableRef m_origin_table;
124     LinkListColumn* m_origin_column;
125
126     using HandoverPatch = LinkViewHandoverPatch;
127     static void generate_patch(const ConstLinkViewRef& ref, std::unique_ptr<HandoverPatch>& patch);
128     static LinkViewRef create_from_and_consume_patch(std::unique_ptr<HandoverPatch>& patch, Group& group);
129
130     void detach();
131     void set_origin_row_index(size_t row_ndx) noexcept;
132
133     void do_insert(size_t link_ndx, size_t target_row_ndx);
134     size_t do_set(size_t link_ndx, size_t target_row_ndx);
135     size_t do_remove(size_t link_ndx);
136     void do_clear(bool broken_reciprocal_backlinks);
137
138     void do_nullify_link(size_t old_target_row_ndx);
139     void do_update_link(size_t old_target_row_ndx, size_t new_target_row_ndx);
140     void do_swap_link(size_t target_row_ndx_1, size_t target_row_ndx_2);
141
142     void refresh_accessor_tree(size_t new_row_ndx) noexcept;
143
144     void update_from_parent(size_t old_baseline) noexcept;
145
146     Replication* get_repl() noexcept;
147     void repl_unselect() noexcept;
148     friend class _impl::TransactLogConvenientEncoder;
149
150 #ifdef REALM_DEBUG
151     void verify(size_t row_ndx) const;
152 #endif
153     // allocate using make_shared:
154     static std::shared_ptr<LinkView> create(Table* origin_table, LinkListColumn&, size_t row_ndx);
155     static std::shared_ptr<LinkView> create_detached();
156
157     friend class _impl::LinkListFriend;
158     friend class LinkListColumn;
159     friend class LangBindHelper;
160     friend class SharedGroup;
161     friend class Query;
162     friend class TableViewBase;
163
164     // must be public for use by make_shared, but cannot be called from outside,
165     // because ctor_cookie is private
166 public:
167     LinkView(const ctor_cookie&, Table* origin_table, LinkListColumn&, size_t row_ndx);
168     LinkView(const ctor_cookie&);
169 };
170
171
172 // Implementation
173
174 inline LinkView::LinkView(const ctor_cookie&, Table* origin_table, LinkListColumn& column, size_t row_ndx)
175     : RowIndexes(IntegerColumn::unattached_root_tag(), column.get_alloc()) // Throws
176     , m_origin_table(origin_table->get_table_ref())
177     , m_origin_column(&column)
178 {
179     m_row_indexes.set_parent(m_origin_column, row_ndx);
180     m_row_indexes.init_from_parent();
181 }
182
183 // create a detached LinkView. Only partially initialized, as it will never be used for
184 // anything, but indicating that it is detached.
185 inline LinkView::LinkView(const ctor_cookie&)
186     : RowIndexes(IntegerColumn::unattached_root_tag(), Allocator::get_default()) // Throws
187     , m_origin_table(TableRef())
188     , m_origin_column(nullptr)
189 {
190 }
191
192 inline std::shared_ptr<LinkView> LinkView::create(Table* origin_table, LinkListColumn& column, size_t row_ndx)
193 {
194     return std::make_shared<LinkView>(ctor_cookie(), origin_table, column, row_ndx);
195 }
196
197 inline std::shared_ptr<LinkView> LinkView::create_detached()
198 {
199     return std::make_shared<LinkView>(ctor_cookie());
200 }
201
202 inline LinkView::~LinkView() noexcept
203 {
204     if (is_attached()) {
205         repl_unselect();
206         m_origin_column->unregister_linkview();
207     }
208 }
209
210 inline void LinkView::detach()
211 {
212     REALM_ASSERT(is_attached());
213     repl_unselect();
214     m_origin_table.reset();
215     m_row_indexes.detach();
216 }
217
218 inline bool LinkView::is_attached() const noexcept
219 {
220     return static_cast<bool>(m_origin_table);
221 }
222
223 inline bool LinkView::is_empty() const noexcept
224 {
225     if (!is_attached())
226         return true;
227
228     if (!m_row_indexes.is_attached())
229         return true;
230
231     return m_row_indexes.is_empty();
232 }
233
234 inline size_t LinkView::size() const noexcept
235 {
236     if (!is_attached())
237         return 0;
238
239     if (!m_row_indexes.is_attached())
240         return 0;
241
242     return m_row_indexes.size();
243 }
244
245 inline bool LinkView::operator==(const LinkView& link_list) const noexcept
246 {
247     Table& target_table_1 = m_origin_column->get_target_table();
248     Table& target_table_2 = link_list.m_origin_column->get_target_table();
249     if (target_table_1.get_index_in_group() != target_table_2.get_index_in_group())
250         return false;
251     if (!m_row_indexes.is_attached() || m_row_indexes.is_empty()) {
252         return !link_list.m_row_indexes.is_attached() || link_list.m_row_indexes.is_empty();
253     }
254     return link_list.m_row_indexes.is_attached() && m_row_indexes.compare(link_list.m_row_indexes);
255 }
256
257 inline bool LinkView::operator!=(const LinkView& link_list) const noexcept
258 {
259     return !(*this == link_list);
260 }
261
262 inline Table::ConstRowExpr LinkView::get(size_t link_ndx) const noexcept
263 {
264     return const_cast<LinkView*>(this)->get(link_ndx);
265 }
266
267 inline Table::RowExpr LinkView::get(size_t link_ndx) noexcept
268 {
269     REALM_ASSERT(is_attached());
270     REALM_ASSERT(m_row_indexes.is_attached());
271     REALM_ASSERT_3(link_ndx, <, m_row_indexes.size());
272
273     Table& target_table = m_origin_column->get_target_table();
274     size_t target_row_ndx = to_size_t(m_row_indexes.get(link_ndx));
275     return target_table[target_row_ndx];
276 }
277
278 inline Table::ConstRowExpr LinkView::operator[](size_t link_ndx) const noexcept
279 {
280     return get(link_ndx);
281 }
282
283 inline Table::RowExpr LinkView::operator[](size_t link_ndx) noexcept
284 {
285     return get(link_ndx);
286 }
287
288 inline void LinkView::add(size_t target_row_ndx)
289 {
290     REALM_ASSERT(is_attached());
291     size_t ins_pos = (m_row_indexes.is_attached()) ? m_row_indexes.size() : 0;
292     insert(ins_pos, target_row_ndx);
293 }
294
295 inline size_t LinkView::find(size_t target_row_ndx, size_t start) const noexcept
296 {
297     REALM_ASSERT(is_attached());
298     REALM_ASSERT_3(target_row_ndx, <, m_origin_column->get_target_table().size());
299     REALM_ASSERT_3(start, <=, size());
300
301     if (!m_row_indexes.is_attached())
302         return not_found;
303
304     return m_row_indexes.find_first(target_row_ndx, start);
305 }
306
307 inline const ColumnBase& LinkView::get_column_base(size_t index) const
308 {
309     return get_target_table().get_column_base(index);
310 }
311
312 inline const Table& LinkView::get_origin_table() const noexcept
313 {
314     return *m_origin_table;
315 }
316
317 inline Table& LinkView::get_origin_table() noexcept
318 {
319     return *m_origin_table;
320 }
321
322 inline size_t LinkView::get_origin_row_index() const noexcept
323 {
324     REALM_ASSERT(is_attached());
325     return m_row_indexes.get_root_array()->get_ndx_in_parent();
326 }
327
328 inline void LinkView::set_origin_row_index(size_t row_ndx) noexcept
329 {
330     REALM_ASSERT(is_attached());
331     m_row_indexes.get_root_array()->set_ndx_in_parent(row_ndx);
332 }
333
334 inline const Table& LinkView::get_target_table() const noexcept
335 {
336     return m_origin_column->get_target_table();
337 }
338
339 inline Table& LinkView::get_target_table() noexcept
340 {
341     return m_origin_column->get_target_table();
342 }
343
344 inline void LinkView::refresh_accessor_tree(size_t new_row_ndx) noexcept
345 {
346     set_origin_row_index(new_row_ndx);
347     m_row_indexes.init_from_parent();
348 }
349
350 inline void LinkView::update_from_parent(size_t old_baseline) noexcept
351 {
352     if (m_row_indexes.is_attached())
353         m_row_indexes.update_from_parent(old_baseline);
354 }
355
356 inline Replication* LinkView::get_repl() noexcept
357 {
358     typedef _impl::TableFriend tf;
359     return tf::get_repl(*m_origin_table);
360 }
361
362
363 // The purpose of this class is to give internal access to some, but not all of
364 // the non-public parts of LinkView.
365 class _impl::LinkListFriend {
366 public:
367     static void do_set(LinkView& list, size_t link_ndx, size_t target_row_ndx)
368     {
369         list.do_set(link_ndx, target_row_ndx);
370     }
371
372     static void do_remove(LinkView& list, size_t link_ndx)
373     {
374         list.do_remove(link_ndx);
375     }
376
377     static void do_clear(LinkView& list)
378     {
379         bool broken_reciprocal_backlinks = false;
380         list.do_clear(broken_reciprocal_backlinks);
381     }
382
383     static void do_insert(LinkView& list, size_t link_ndx, size_t target_row_ndx)
384     {
385         list.do_insert(link_ndx, target_row_ndx);
386     }
387
388     static const LinkListColumn& get_origin_column(const LinkView& list)
389     {
390         REALM_ASSERT(list.is_attached());
391         return *list.m_origin_column;
392     }
393 };
394
395 } // namespace realm
396
397 #endif // REALM_LINK_VIEW_HPP