//////////////////////////////////////////////////////////////////////////// // // 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 BINDING_CONTEXT_HPP #define BINDING_CONTEXT_HPP #include <realm/object-store/index_set.hpp> #include <realm/keys.hpp> #include <memory> #include <tuple> #include <unordered_map> #include <vector> namespace realm { // BindingContext is the extension point for adding binding-specific behavior to // a SharedRealm. It can be used to store additional data associated with the // Realm which is needed by the binding, and there are several methods which // can be overridden to receive notifications of state changes within the Realm. // // A simple implementation which lets the user register functions to be // called on refresh could look like the following: // // class BindingContextImplementation : public BindingContext { // public: // // A token returned from add_notification that can be used to remove the // // notification later // struct token : private std::list<util::UniqueFunction<void ()>>::iterator { // token(std::list<util::UniqueFunction<void ()>>::iterator it) // : std::list<util::UniqueFunction<void ()>>::iterator(it) { } // friend class DelegateImplementation; // }; // // token add_notification(util::UniqueFunction<void ()> func) // { // m_registered_notifications.push_back(std::move(func)); // return token(std::prev(m_registered_notifications.end())); // } // // void remove_notification(token entry) // { // m_registered_notifications.erase(entry); // } // // // Override the did_change method to call each registered notification // void did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) override // { // // Loop oddly so that unregistering a notification from within the // // registered function works // for (auto it = m_registered_notifications.begin(); it != m_registered_notifications.end(); ) { // (*it++)(); // } // } // // private: // std::list<util::UniqueFunction<void ()>> m_registered_notifications; // }; class Realm; class Schema; class BindingContext { public: virtual ~BindingContext() = default; std::weak_ptr<Realm> realm; // Called when the Realm is about to send notifications about Realm, // Collection or Object changes. This method will be called even if // no notification callbacks have been registered. virtual void will_send_notifications() {} // Called when the Realm is done sending all change notifications. This method // will be called even if no notification callbacks have been registered. virtual void did_send_notifications() {} // Called by the Realm when refresh called or a notification arrives which // is triggered through write transaction committed by itself or a different // Realm instance. virtual void before_notify() {} // Called by the Realm when a write transaction is committed to the file by // a different Realm instance (possibly in a different process) virtual void changes_available() {} struct ObserverState; // Override this function if you want to receive detailed information about // external changes to a specific set of objects. // This is called before each operation which may advance the read // transaction to include // ObserverStates for each row for which detailed change information is // desired. virtual std::vector<ObserverState> get_observed_rows() { return {}; } // Called immediately before the read transaction is advanced if detailed // change information was requested (by returning a non-empty array from // get_observed_rows()). // The observers vector is the vector returned by get_observed_row(), // updated with change information. The invalidated vector is a list of the // `info` fields of observed rows which will be deleted. virtual void will_change(std::vector<ObserverState> const& observers, std::vector<void*> const& invalidated); // Called immediately after the read transaction version is advanced. Unlike // will_change(), this is called even if detailed change information was not // requested or if the Realm is not actually in a read transaction, although // both vectors will be empty in that case. virtual void did_change(std::vector<ObserverState> const& observers, std::vector<void*> const& invalidated, bool version_changed = true); // Called immediately after the corresponding Realm's schema is changed through // update_schema()/set_schema_subset() or the schema is changed by another Realm // instance. The parameter is a schema reference which is the same as the return // value of Realm::schema(). virtual void schema_did_change(Schema const&) {} // Change information for a single field of a row struct ColumnInfo { // What kind of change occurred? // Always Set or None for everything but LinkList columns. enum class Kind { None, // No change Set, // The value or entries at `indices` were assigned to Insert, // New values were inserted at each of the indices given Remove, // Values were removed at each of the indices given SetAll // The entire LinkList has been replaced with a new set of values } kind = Kind::None; // The indices where things happened for Set, Insert and Remove on // LinkList columns. Not used for other types or for None or SetAll. IndexSet indices; }; // Information about an observed object in a table // // Each object which needs detailed change information should have an // ObserverState entry in the vector returned from get_observed_rows(), with // the initial table and row indexes set (and optionally the info field). // The Realm parses the transaction log, and populates the `changes` vector // in each ObserverState with information about what changes were made. struct ObserverState { // Table and object which is observed realm::TableKey table_key; ObjKey obj_key; // Opaque userdata for the delegate's use void* info; // Populated with information about which columns were changed // May be shorter than the actual number of columns if the later columns // are not modified std::unordered_map<int64_t, ColumnInfo> changes; // Simple lexographic ordering friend bool operator<(ObserverState const& lft, ObserverState const& rgt) { return std::tie(lft.table_key, lft.obj_key) < std::tie(rgt.table_key, rgt.obj_key); } }; }; inline void BindingContext::will_change(std::vector<ObserverState> const&, std::vector<void*> const&) {} inline void BindingContext::did_change(std::vector<ObserverState> const&, std::vector<void*> const&, bool) {} } // namespace realm #endif /* BINDING_CONTEXT_HPP */