//////////////////////////////////////////////////////////////////////////// // // 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_object_accessor.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" namespace realm { namespace js { template class NativeAccessor; 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 Type = T; 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; using Arguments = js::Arguments; static ObjectType create_instance(ContextType, realm::List); // properties static void get_length(ContextType, ObjectType, ReturnValue &); static void get_type(ContextType, ObjectType, ReturnValue &); static void get_optional(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, ObjectType, Arguments &, ReturnValue &); static void pop(ContextType, ObjectType, Arguments &, ReturnValue &); static void unshift(ContextType, ObjectType, Arguments &, ReturnValue &); static void shift(ContextType, ObjectType, Arguments &, ReturnValue &); static void splice(ContextType, ObjectType, Arguments &, ReturnValue &); static void snapshot(ContextType, ObjectType, Arguments &, ReturnValue &); static void filtered(ContextType, ObjectType, Arguments &, ReturnValue &); static void sorted(ContextType, ObjectType, Arguments &, ReturnValue &); static void is_valid(ContextType, ObjectType, Arguments &, ReturnValue &); static void is_empty(ContextType, ObjectType, Arguments &, ReturnValue &); static void index_of(ContextType, ObjectType, Arguments &, ReturnValue &); // observable static void add_listener(ContextType, ObjectType, Arguments &, ReturnValue &); static void remove_listener(ContextType, ObjectType, Arguments &, ReturnValue &); static void remove_all_listeners(ContextType, ObjectType, Arguments &, 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}, {"isEmpty", wrap}, {"indexOf", wrap}, {"min", wrap, AggregateFunc::Min>>}, {"max", wrap, AggregateFunc::Max>>}, {"sum", wrap, AggregateFunc::Sum>>}, {"avg", wrap, AggregateFunc::Avg>>}, {"addListener", wrap}, {"removeListener", wrap}, {"removeAllListeners", wrap}, }; PropertyMap const properties = { {"length", {wrap, nullptr}}, {"type", {wrap, nullptr}}, {"optional", {wrap, nullptr}}, }; IndexPropertyType const index_accessor = {wrap, wrap}; private: static void validate_value(ContextType, realm::List&, ValueType); }; 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, ObjectType object, ReturnValue &return_value) { auto list = get_internal>(object); return_value.set((uint32_t)list->size()); } template void ListClass::get_type(ContextType ctx, ObjectType object, ReturnValue &return_value) { auto list = get_internal>(object); return_value.set(string_for_property_type(list->get_type() & ~realm::PropertyType::Flags)); } template void ListClass::get_optional(ContextType, ObjectType object, ReturnValue &return_value) { auto list = get_internal>(object); return_value.set(is_nullable(list->get_type())); } template void ListClass::get_index(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) { auto list = get_internal>(object); NativeAccessor accessor(ctx, *list); return_value.set(list->get(accessor, index)); } template bool ListClass::set_index(ContextType ctx, ObjectType object, uint32_t index, ValueType value) { auto list = get_internal>(object); validate_value(ctx, *list, value); NativeAccessor accessor(ctx, *list); list->set(accessor, index, value); return true; } template void ListClass::push(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { auto list = get_internal>(this_object); for (size_t i = 0; i < args.count; i++) { validate_value(ctx, *list, args[i]); } NativeAccessor accessor(ctx, *list); for (size_t i = 0; i < args.count; i++) { list->add(accessor, args[i]); } return_value.set((uint32_t)list->size()); } template void ListClass::pop(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { args.validate_maximum(0); auto list = get_internal>(this_object); auto size = static_cast(list->size()); if (size == 0) { list->verify_in_transaction(); return_value.set_undefined(); } else { get_index(ctx, this_object, size - 1, return_value); list->remove(size - 1); } } template void ListClass::unshift(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { auto list = get_internal>(this_object); for (size_t i = 0; i < args.count; i++) { validate_value(ctx, *list, args[i]); } NativeAccessor accessor(ctx, *list); for (size_t i = 0; i < args.count; i++) { list->insert(accessor, i, args[i]); } return_value.set((uint32_t)list->size()); } template void ListClass::shift(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { args.validate_maximum(0); auto list = get_internal>(this_object); if (list->size() == 0) { list->verify_in_transaction(); return_value.set_undefined(); } else { get_index(ctx, this_object, 0, return_value); list->remove(0); } } template void ListClass::splice(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { auto list = get_internal>(this_object); size_t size = list->size(); long index = std::min(Value::to_number(ctx, args[0]), size); if (index < 0) { index = std::max(size + index, 0); } size_t remove; if (args.count < 2) { remove = size - index; } else { remove = std::max(Value::to_number(ctx, args[1]), 0); remove = std::min(remove, size - index); } std::vector removed_objects; removed_objects.reserve(remove); NativeAccessor accessor(ctx, *list); for (size_t i = 0; i < remove; i++) { removed_objects.push_back(list->get(accessor, index)); list->remove(index); } for (size_t i = 2; i < args.count; i++) { list->insert(accessor, index + i - 2, args[i]); } return_value.set(Object::create_array(ctx, removed_objects)); } template void ListClass::snapshot(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { args.validate_maximum(0); auto list = get_internal>(this_object); return_value.set(ResultsClass::create_instance(ctx, list->snapshot())); } template void ListClass::filtered(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { auto list = get_internal>(this_object); return_value.set(ResultsClass::create_filtered(ctx, *list, args)); } template void ListClass::sorted(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { auto list = get_internal>(this_object); return_value.set(ResultsClass::create_instance(ctx, list->sort(ResultsClass::get_keypaths(ctx, args)))); } template void ListClass::is_valid(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { return_value.set(get_internal>(this_object)->is_valid()); } template void ListClass::is_empty(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { return_value.set(get_internal>(this_object)->size() == 0); } template void ListClass::index_of(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { auto fn = [&](auto&& row) { auto list = get_internal>(this_object); NativeAccessor accessor(ctx, *list); return list->find(accessor, row); }; ResultsClass::index_of(ctx, fn, args, return_value); } template void ListClass::add_listener(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { auto list = get_internal>(this_object); ResultsClass::add_listener(ctx, *list, this_object, args); } template void ListClass::remove_listener(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { auto list = get_internal>(this_object); ResultsClass::remove_listener(ctx, *list, this_object, args); } template void ListClass::remove_all_listeners(ContextType ctx, ObjectType this_object, Arguments &args, ReturnValue &return_value) { args.validate_maximum(0); auto list = get_internal>(this_object); list->m_notification_tokens.clear(); } template void ListClass::validate_value(ContextType ctx, realm::List& list, ValueType value) { auto type = list.get_type(); StringData object_type; if (type == realm::PropertyType::Object) { object_type = list.get_object_schema().name; } if (!Value::is_valid_for_property_type(ctx, value, type, object_type)) { throw TypeErrorException("Property", object_type ? object_type : string_for_property_type(type), Value::to_string(ctx, value)); } } } // js } // realm