//////////////////////////////////////////////////////////////////////////// // // Copyright 2016 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. // //////////////////////////////////////////////////////////////////////////// #pragma once #include "js_collection.hpp" #include "js_realm_object.hpp" #include "js_results.hpp" #include "js_types.hpp" #include "js_util.hpp" #include "shared_realm.hpp" #include "list.hpp" #include "parser.hpp" #include "query_builder.hpp" namespace realm { namespace js { template class List : public realm::List { public: List(std::shared_ptr r, const ObjectSchema& s, LinkViewRef l) noexcept : realm::List(r, l) {} List(const realm::List &l) : realm::List(l) {} std::vector, NotificationToken>> m_notification_tokens; }; template struct ListClass : ClassDefinition, CollectionClass> { using ContextType = typename T::Context; using ObjectType = typename T::Object; using ValueType = typename T::Value; using FunctionType = typename T::Function; using Object = js::Object; using Value = js::Value; using ReturnValue = js::ReturnValue; static ObjectType create_instance(ContextType, realm::List); // properties static void get_length(ContextType, ObjectType, ReturnValue &); static void get_index(ContextType, ObjectType, uint32_t, ReturnValue &); static bool set_index(ContextType, ObjectType, uint32_t, ValueType); // methods static void push(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void pop(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void unshift(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void shift(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void splice(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void snapshot(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void filtered(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void sorted(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void is_valid(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); // observable static void add_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void remove_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void remove_all_listeners(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); std::string const name = "List"; MethodMap const methods = { {"push", wrap}, {"pop", wrap}, {"unshift", wrap}, {"shift", wrap}, {"splice", wrap}, {"snapshot", wrap}, {"filtered", wrap}, {"sorted", wrap}, {"isValid", wrap}, {"addListener", wrap}, {"removeListener", wrap}, {"removeAllListeners", wrap}, }; PropertyMap const properties = { {"length", {wrap, nullptr}}, }; IndexPropertyType const index_accessor = {wrap, wrap}; }; template typename T::Object ListClass::create_instance(ContextType ctx, realm::List list) { return create_object>(ctx, new realm::js::List(std::move(list))); } template void ListClass::get_length(ContextType ctx, ObjectType object, ReturnValue &return_value) { auto list = get_internal>(object); return_value.set((uint32_t)list->size()); } template void ListClass::get_index(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) { auto list = get_internal>(object); auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); return_value.set(RealmObjectClass::create_instance(ctx, std::move(realm_object))); } template bool ListClass::set_index(ContextType ctx, ObjectType object, uint32_t index, ValueType value) { auto list = get_internal>(object); list->set(ctx, value, index); return true; } template void ListClass::push(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); for (size_t i = 0; i < argc; i++) { list->add(ctx, arguments[i]); } return_value.set((uint32_t)list->size()); } template void ListClass::pop(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal>(this_object); size_t size = list->size(); if (size == 0) { list->verify_in_transaction(); return_value.set_undefined(); } else { size_t index = size - 1; auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); return_value.set(RealmObjectClass::create_instance(ctx, std::move(realm_object))); list->remove(index); } } template void ListClass::unshift(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); for (size_t i = 0; i < argc; i++) { list->insert(ctx, arguments[i], i); } return_value.set((uint32_t)list->size()); } template void ListClass::shift(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal>(this_object); if (list->size() == 0) { list->verify_in_transaction(); return_value.set_undefined(); } else { auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(0)); return_value.set(RealmObjectClass::create_instance(ctx, std::move(realm_object))); list->remove(0); } } template void ListClass::splice(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); size_t size = list->size(); long index = std::min(Value::to_number(ctx, arguments[0]), size); if (index < 0) { index = std::max(size + index, 0); } size_t remove; if (argc < 2) { remove = size - index; } else { remove = std::max(Value::to_number(ctx, arguments[1]), 0); remove = std::min(remove, size - index); } std::vector removed_objects; removed_objects.reserve(remove); for (size_t i = 0; i < remove; i++) { auto realm_object = realm::Object(list->get_realm(), list->get_object_schema(), list->get(index)); removed_objects.push_back(RealmObjectClass::create_instance(ctx, std::move(realm_object))); list->remove(index); } for (size_t i = 2; i < argc; i++) { list->insert(ctx, arguments[i], index + i - 2); } return_value.set(Object::create_array(ctx, removed_objects)); } template void ListClass::snapshot(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal>(this_object); return_value.set(ResultsClass::create_instance(ctx, list->snapshot())); } template void ListClass::filtered(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count_at_least(argc, 1); auto list = get_internal>(this_object); return_value.set(ResultsClass::create_filtered(ctx, *list, argc, arguments)); } template void ListClass::sorted(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1, 2); auto list = get_internal>(this_object); return_value.set(ResultsClass::create_sorted(ctx, *list, argc, arguments)); } template void ListClass::is_valid(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { return_value.set(get_internal>(this_object)->is_valid()); } template void ListClass::add_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); auto list = get_internal>(this_object); auto callback = Value::validated_to_function(ctx, arguments[0]); Protected protected_callback(ctx, callback); Protected protected_this(ctx, this_object); Protected protected_ctx(Context::get_global_context(ctx)); auto token = list->add_notification_callback([=](CollectionChangeSet change_set, std::exception_ptr exception) { HANDLESCOPE ValueType arguments[2]; arguments[0] = static_cast(protected_this); arguments[1] = CollectionClass::create_collection_change_set(protected_ctx, change_set); Function::call(protected_ctx, protected_callback, protected_this, 2, arguments); }); list->m_notification_tokens.emplace_back(protected_callback, std::move(token)); } template void ListClass::remove_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 1); auto list = get_internal>(this_object); auto callback = Value::validated_to_function(ctx, arguments[0]); auto protected_function = Protected(ctx, callback); auto iter = list->m_notification_tokens.begin(); typename Protected::Comparator compare; while (iter != list->m_notification_tokens.end()) { if(compare(iter->first, protected_function)) { iter = list->m_notification_tokens.erase(iter); } else { iter++; } } } template void ListClass::remove_all_listeners(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 0); auto list = get_internal>(this_object); list->m_notification_tokens.clear(); } } // js } // realm