1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2017 Realm Inc.
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
17 ////////////////////////////////////////////////////////////////////////////
19 #ifndef REALM_OS_OBJECT_ACCESSOR_IMPL_HPP
20 #define REALM_OS_OBJECT_ACCESSOR_IMPL_HPP
22 #include "object_accessor.hpp"
24 #include <realm/util/any.hpp>
27 using AnyDict = std::map<std::string, util::Any>;
28 using AnyVector = std::vector<util::Any>;
30 // An object accessor context which can be used to create and access objects
31 // using util::Any as the type-erased value type. In addition, this serves as
32 // the reference implementation of an accessor context that must be implemented
36 // This constructor is the only one used by the object accessor code, and is
37 // used when recurring into a link or array property during object creation
38 // (i.e. prop.type will always be Object or Array).
39 CppContext(CppContext& c, Property const& prop)
41 , object_schema(prop.type == PropertyType::Object ? &*realm->schema().find(prop.object_type) : c.object_schema)
44 CppContext() = default;
45 CppContext(std::shared_ptr<Realm> realm, const ObjectSchema* os=nullptr)
46 : realm(std::move(realm)), object_schema(os) { }
48 // The use of util::Optional for the following two functions is not a hard
49 // requirement; only that it be some type which can be evaluated in a
50 // boolean context to determine if it contains a value, and if it does
51 // contain a value it must be dereferencable to obtain that value.
53 // Get the value for a property in an input object, or `util::none` if no
54 // value present. The property is identified both by the name of the
55 // property and its index within the ObjectScehma's persisted_properties
57 util::Optional<util::Any> value_for_property(util::Any& dict,
58 std::string const& prop_name,
59 size_t /* property_index */) const
61 auto const& v = any_cast<AnyDict&>(dict);
62 auto it = v.find(prop_name);
63 return it == v.end() ? util::none : util::make_optional(it->second);
66 // Get the default value for the given property in the given object schema,
67 // or `util::none` if there is none (which is distinct from the default
70 // This implementation does not support default values; see the default
71 // value tests for an example of one which does.
72 util::Optional<util::Any>
73 default_value_for_property(ObjectSchema const&, std::string const&) const
78 // Invoke `fn` with each of the values from an enumerable type
79 template<typename Func>
80 void enumerate_list(util::Any& value, Func&& fn) {
81 for (auto&& v : any_cast<AnyVector&>(value))
85 // Determine if `value` boxes the same List as `list`
86 bool is_same_list(List const& list, util::Any const& value)
88 if (auto list2 = any_cast<List>(&value))
89 return list == *list2;
93 // Convert from core types to the boxed type
94 util::Any box(BinaryData v) const { return std::string(v); }
95 util::Any box(List v) const { return v; }
96 util::Any box(Object v) const { return v; }
97 util::Any box(Results v) const { return v; }
98 util::Any box(StringData v) const { return std::string(v); }
99 util::Any box(Timestamp v) const { return v; }
100 util::Any box(bool v) const { return v; }
101 util::Any box(double v) const { return v; }
102 util::Any box(float v) const { return v; }
103 util::Any box(int64_t v) const { return v; }
104 util::Any box(util::Optional<bool> v) const { return v; }
105 util::Any box(util::Optional<double> v) const { return v; }
106 util::Any box(util::Optional<float> v) const { return v; }
107 util::Any box(util::Optional<int64_t> v) const { return v; }
108 util::Any box(RowExpr) const;
110 // Any properties are only supported by the Cocoa binding to enable reading
111 // old Realm files that may have used them. Other bindings can safely not
113 util::Any box(Mixed) const { REALM_TERMINATE("not supported"); }
115 // Convert from the boxed type to core types. This needs to be implemented
116 // for all of the types which `box()` can take, plus `RowExpr` and optional
117 // versions of the numeric types, minus `List` and `Results`.
119 // `create` and `update` are only applicable to `unbox<RowExpr>`. If
120 // `create` is false then when given something which is not a managed Realm
121 // object `unbox()` should simply return a detached row expr, while if it's
122 // true then `unbox()` should create a new object in the context's Realm
123 // using the provided value. If `update` is true then upsert semantics
124 // should be used for this.
126 T unbox(util::Any& v, bool /*create*/= false, bool /*update*/= false) const { return any_cast<T>(v); }
128 bool is_null(util::Any const& v) const noexcept { return !v.has_value(); }
129 util::Any null_value() const noexcept { return {}; }
130 util::Optional<util::Any> no_value() const noexcept { return {}; }
132 // KVO hooks which will be called before and after modying a property from
133 // within Object::create().
134 void will_change(Object const&, Property const&) {}
137 // Get a string representation of the given value for use in error messages.
138 std::string print(util::Any const&) const { return "not implemented"; }
140 // Cocoa allows supplying fewer values than there are properties when
141 // creating objects using an array of values. Other bindings should not
142 // mimick this behavior so just return false here.
143 bool allow_missing(util::Any const&) const { return false; }
146 std::shared_ptr<Realm> realm;
147 const ObjectSchema* object_schema = nullptr;
151 inline util::Any CppContext::box(RowExpr row) const
153 REALM_ASSERT(object_schema);
154 return Object(realm, *object_schema, row);
158 inline StringData CppContext::unbox(util::Any& v, bool, bool) const
162 auto& value = any_cast<std::string&>(v);
163 return StringData(value.c_str(), value.size());
167 inline BinaryData CppContext::unbox(util::Any& v, bool, bool) const
171 auto& value = any_cast<std::string&>(v);
172 return BinaryData(value.c_str(), value.size());
176 inline RowExpr CppContext::unbox(util::Any& v, bool create, bool update) const
178 if (auto object = any_cast<Object>(&v))
179 return object->row();
180 if (auto row = any_cast<RowExpr>(&v))
185 REALM_ASSERT(object_schema);
186 return Object::create(const_cast<CppContext&>(*this), realm, *object_schema, v, update).row();
190 inline util::Optional<bool> CppContext::unbox(util::Any& v, bool, bool) const
192 return v.has_value() ? util::make_optional(unbox<bool>(v)) : util::none;
196 inline util::Optional<int64_t> CppContext::unbox(util::Any& v, bool, bool) const
198 return v.has_value() ? util::make_optional(unbox<int64_t>(v)) : util::none;
202 inline util::Optional<double> CppContext::unbox(util::Any& v, bool, bool) const
204 return v.has_value() ? util::make_optional(unbox<double>(v)) : util::none;
208 inline util::Optional<float> CppContext::unbox(util::Any& v, bool, bool) const
210 return v.has_value() ? util::make_optional(unbox<float>(v)) : util::none;
214 inline Mixed CppContext::unbox(util::Any&, bool, bool) const
216 throw std::logic_error("'Any' type is unsupported");
220 #endif // REALM_OS_OBJECT_ACCESSOR_IMPL_HPP