Newer
Older
bremer-ios-app / Pods / Realm / core / realm-monorepo.xcframework / watchos-arm64_armv7k_arm64_32 / Headers / realm / object-store / dictionary.hpp
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2020 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_DICTIONARY_HPP
#define REALM_OS_DICTIONARY_HPP

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

namespace realm {

struct DictionaryChangeSet {
    DictionaryChangeSet(size_t max_keys)
    {
        m_string_store.reserve(max_keys);
    }
    DictionaryChangeSet()
        : DictionaryChangeSet(0)
    {
    }

    DictionaryChangeSet(const DictionaryChangeSet&);
    DictionaryChangeSet(DictionaryChangeSet&&) = default;

    DictionaryChangeSet& operator=(const DictionaryChangeSet&);
    DictionaryChangeSet& operator=(DictionaryChangeSet&&) = default;

    // Keys which were removed from the _old_ dictionary
    std::vector<Mixed> deletions;

    // Keys in the _new_ dictionary which are new insertions
    std::vector<Mixed> insertions;

    // Keys of objects/values which were modified
    std::vector<Mixed> modifications;

    bool collection_root_was_deleted = false;

    void add_deletion(const Mixed& key)
    {
        add(deletions, key);
    }
    void add_insertion(const Mixed& key)
    {
        add(insertions, key);
    }
    void add_modification(const Mixed& key)
    {
        add(modifications, key);
    }

private:
    void add(std::vector<Mixed>& arr, const Mixed& key);
    std::vector<std::string> m_string_store;
};

namespace object_store {

class Dictionary : public object_store::Collection {
public:
    using Iterator = realm::Dictionary::Iterator;
    using Collection::Collection;
    Dictionary()
        : Collection(PropertyType::Dictionary)
    {
    }

    bool operator==(const Dictionary& rgt) const noexcept;
    bool operator!=(const Dictionary& rgt) const noexcept;

    template <typename T>
    void insert(StringData key, T value);
    std::pair<size_t, bool> insert_any(StringData key, Mixed value);

    template <typename T>
    T get(StringData key) const;

    Obj insert_embedded(StringData key);
    void erase(StringData key);
    bool try_erase(StringData key);
    void remove_all();
    Obj get_object(StringData key);
    Mixed get_any(StringData key);
    Mixed get_any(size_t ndx) const final;
    util::Optional<Mixed> try_get_any(StringData key) const;
    std::pair<StringData, Mixed> get_pair(size_t ndx) const;
    size_t find_any(Mixed value) const final;
    bool contains(StringData key) const;

    template <typename T, typename Context>
    void insert(Context&, StringData key, T&& value, CreatePolicy = CreatePolicy::SetLink);
    template <typename Context>
    auto get(Context&, StringData key) const;

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

    Results snapshot() const;
    Dictionary freeze(const std::shared_ptr<Realm>& realm) const;
    Results get_keys() const;
    Results get_values() const;

    using CBFunc = util::UniqueFunction<void(DictionaryChangeSet)>;
    NotificationToken
    add_key_based_notification_callback(CBFunc cb, std::optional<KeyPathArray> key_path_array = std::nullopt) &;

    Iterator begin() const;
    Iterator end() const;

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

    realm::Dictionary& dict() const noexcept
    {
        REALM_ASSERT_DEBUG(dynamic_cast<realm::Dictionary*>(m_coll_base.get()));
        return static_cast<realm::Dictionary&>(*m_coll_base);
    }

    template <typename Fn>
    auto dispatch(Fn&&) const;
    Obj get_object(StringData key) const;
};


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

template <typename T>
void Dictionary::insert(StringData key, T value)
{
    verify_in_transaction();
    dict().insert(key, value);
}

template <typename T>
T Dictionary::get(StringData key) const
{
    auto res = dict().get(key);
    if (res.is_null()) {
        if constexpr (std::is_same_v<T, Decimal128>) {
            return Decimal128{realm::null()};
        }
        else {
            return T{};
        }
    }
    return res.get<T>();
}

template <>
inline Obj Dictionary::get<Obj>(StringData key) const
{
    return get_object(key);
}

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

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

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

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

    if (!policy.diff)
        remove_all();

    ctx.enumerate_dictionary(values, [&](StringData key, auto&& value) {
        if (policy.diff) {
            util::Optional<Mixed> old_value = dict().try_get(key);
            auto new_value = ctx.template unbox<Mixed>(value);
            if (!old_value || *old_value != new_value) {
                dict().insert(key, new_value);
            }
        }
        else {
            this->insert(ctx, key, value, policy);
        }
    });
}

} // namespace object_store
} // namespace realm


#endif /* REALM_OS_DICTIONARY_HPP */