Newer
Older
bremer-ios-app / Pods / Realm / core / realm-monorepo.xcframework / macos-x86_64_arm64 / Headers / realm / object-store / list.hpp
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2015 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

#ifndef REALM_OS_LIST_HPP
#define REALM_OS_LIST_HPP

#include <realm/object-store/collection.hpp>

#include <realm/decimal128.hpp>
#include <realm/list.hpp>
#include <realm/mixed.hpp>
#include <realm/object_id.hpp>

#include <functional>
#include <memory>

namespace realm {
class Obj;
class Query;
class ThreadSafeReference;
struct ColKey;
struct ObjKey;

class List : public object_store::Collection {
public:
    using object_store::Collection::Collection;
    List()
        : Collection(PropertyType::Array)
    {
    }

    List(const List&);
    List& operator=(const List&);
    List(List&&);
    List& operator=(List&&);

    Query get_query() const;
    ConstTableRef get_table() const;

    void move(size_t source_ndx, size_t dest_ndx);
    void remove(size_t list_ndx);
    void remove_all();
    void swap(size_t ndx1, size_t ndx2);
    void delete_at(size_t list_ndx);
    void delete_all();

    template <typename T = Obj>
    T get(size_t row_ndx) const;

    template <typename T>
    size_t find(T const& value) const;

    // Find the index in the List of the first row matching the query
    size_t find(Query&& query) const;

    template <typename T>
    void add(T value);
    template <typename T>
    void insert(size_t list_ndx, T value);
    template <typename T>
    void set(size_t row_ndx, T value);

    void insert_any(size_t list_ndx, Mixed value);
    void set_any(size_t list_ndx, Mixed value);
    Mixed get_any(size_t list_ndx) const final;
    size_t find_any(Mixed value) const final;

    Results filter(Query q) const;

    // Returns a frozen copy of this List.
    // Equivalent to producing a thread-safe reference and resolving it in the frozen realm.
    List freeze(std::shared_ptr<Realm> const& frozen_realm) const;

    bool operator==(List const& rgt) const noexcept;

    template <typename Context>
    auto get(Context&, size_t row_ndx) const;
    template <typename T, typename Context>
    size_t find(Context&, T&& value) const;

    template <typename T, typename Context>
    void add(Context&, T&& value, CreatePolicy = CreatePolicy::SetLink);
    template <typename T, typename Context>
    void insert(Context&, size_t list_ndx, T&& value, CreatePolicy = CreatePolicy::SetLink);
    template <typename T, typename Context>
    void set(Context&, size_t row_ndx, T&& value, CreatePolicy = CreatePolicy::SetLink);

    Obj add_embedded();
    Obj set_embedded(size_t list_ndx);
    Obj insert_embedded(size_t list_ndx);

    Obj get_object(size_t list_ndx);

    // Replace the values in this list with the values from an enumerable object
    template <typename T, typename Context>
    void assign(Context&, T&& value, CreatePolicy = CreatePolicy::SetLink);

private:
    const char* type_name() const noexcept override
    {
        return "List";
    }

    LstBase& list_base() const noexcept
    {
        REALM_ASSERT_DEBUG(dynamic_cast<LstBase*>(m_coll_base.get()));
        return static_cast<LstBase&>(*m_coll_base);
    }

    template <typename Fn>
    auto dispatch(Fn&&) const;
    template <typename T>
    auto& as() const;

    template <typename T, typename Context>
    void set_if_different(Context&, size_t row_ndx, T&& value, CreatePolicy);

    friend struct std::hash<List>;
};

template <>
Obj List::get(size_t row_ndx) const;

template <typename T>
auto& List::as() const
{
    REALM_ASSERT_DEBUG(dynamic_cast<Lst<T>*>(m_coll_base.get()));
    return static_cast<Lst<T>&>(*m_coll_base);
}

template <>
inline auto& List::as<Obj>() const
{
    REALM_ASSERT_DEBUG(dynamic_cast<LnkLst*>(m_coll_base.get()));
    return static_cast<LnkLst&>(*m_coll_base);
}

template <>
inline auto& List::as<ObjKey>() const
{
    REALM_ASSERT_DEBUG(dynamic_cast<LnkLst*>(m_coll_base.get()));
    return static_cast<LnkLst&>(*m_coll_base);
}

template <typename Fn>
auto List::dispatch(Fn&& fn) const
{
    verify_attached();
    return switch_on_type(get_type(), std::forward<Fn>(fn));
}

template <typename Context>
auto List::get(Context& ctx, size_t row_ndx) const
{
    return dispatch([&](auto t) {
        return ctx.box(this->get<std::decay_t<decltype(*t)>>(row_ndx));
    });
}

template <typename T, typename Context>
size_t List::find(Context& ctx, T&& value) const
{
    return dispatch([&](auto t) {
        return this->find(ctx.template unbox<std::decay_t<decltype(*t)>>(value, CreatePolicy::Skip));
    });
}

template <typename T, typename Context>
void List::add(Context& ctx, T&& value, CreatePolicy policy)
{
    if (m_is_embedded) {
        validate_embedded(ctx, value, policy);
        auto key = as<Obj>().create_and_insert_linked_object(size()).get_key();
        ctx.template unbox<Obj>(value, policy, key);
        return;
    }
    dispatch([&](auto t) {
        this->add(ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
    });
}

template <typename T, typename Context>
void List::insert(Context& ctx, size_t list_ndx, T&& value, CreatePolicy policy)
{
    if (m_is_embedded) {
        validate_embedded(ctx, value, policy);
        auto key = as<Obj>().create_and_insert_linked_object(list_ndx).get_key();
        ctx.template unbox<Obj>(value, policy, key);
        return;
    }
    dispatch([&](auto t) {
        this->insert(list_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
    });
}

template <typename T, typename Context>
void List::set(Context& ctx, size_t list_ndx, T&& value, CreatePolicy policy)
{
    if (m_is_embedded) {
        validate_embedded(ctx, value, policy);

        auto& list = as<Obj>();
        auto key = policy.diff ? list.get(list_ndx) : list.create_and_set_linked_object(list_ndx).get_key();
        ctx.template unbox<Obj>(value, policy, key);
        return;
    }
    dispatch([&](auto t) {
        this->set(list_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, policy));
    });
}

template <typename T, typename Context>
void List::set_if_different(Context& ctx, size_t row_ndx, T&& value, CreatePolicy policy)
{
    if (m_is_embedded) {
        validate_embedded(ctx, value, policy);
        auto key = policy.diff ? this->get<Obj>(row_ndx) : as<Obj>().create_and_set_linked_object(row_ndx);
        ctx.template unbox<Obj>(value, policy, key.get_key());
        return;
    }
    dispatch([&](auto t) {
        using U = std::decay_t<decltype(*t)>;
        if constexpr (std::is_same_v<U, Obj>) {
            auto old_value = this->get<U>(row_ndx);
            auto new_value = ctx.template unbox<U>(value, policy, old_value.get_key());
            if (new_value.get_key() != old_value.get_key())
                this->set(row_ndx, new_value);
        }
        else {
            auto old_value = this->get<U>(row_ndx);
            auto new_value = ctx.template unbox<U>(value, policy);
            if (old_value != new_value)
                this->set(row_ndx, new_value);
        }
    });
}

template <typename T, typename Context>
void List::assign(Context& ctx, T&& values, CreatePolicy policy)
{
    if (ctx.is_same_list(*this, values))
        return;

    if (ctx.is_null(values)) {
        remove_all();
        return;
    }

    if (!policy.diff)
        remove_all();

    size_t sz = size();
    size_t index = 0;
    ctx.enumerate_collection(values, [&](auto&& element) {
        if (index >= sz)
            this->add(ctx, element, policy);
        else if (policy.diff)
            this->set_if_different(ctx, index, element, policy);
        else
            this->set(ctx, index, element, policy);
        index++;
    });
    while (index < sz)
        remove(--sz);
}
} // namespace realm

namespace std {
template <>
struct hash<realm::List> {
    size_t operator()(realm::List const&) const;
};
} // namespace std

#endif // REALM_OS_LIST_HPP