added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / impl / object_accessor_impl.hpp
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2017 Realm Inc.
4 //
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
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
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.
16 //
17 ////////////////////////////////////////////////////////////////////////////
18
19 #ifndef REALM_OS_OBJECT_ACCESSOR_IMPL_HPP
20 #define REALM_OS_OBJECT_ACCESSOR_IMPL_HPP
21
22 #include "object_accessor.hpp"
23
24 #include <realm/util/any.hpp>
25
26 namespace realm {
27 using AnyDict = std::map<std::string, util::Any>;
28 using AnyVector = std::vector<util::Any>;
29
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
33 // by each binding.
34 class CppContext {
35 public:
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)
40     : realm(c.realm)
41     , object_schema(prop.type == PropertyType::Object ? &*realm->schema().find(prop.object_type) : c.object_schema)
42     { }
43
44     CppContext() = default;
45     CppContext(std::shared_ptr<Realm> realm, const ObjectSchema* os=nullptr)
46     : realm(std::move(realm)), object_schema(os) { }
47
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.
52
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
56     // array.
57     util::Optional<util::Any> value_for_property(util::Any& dict,
58                                                  std::string const& prop_name,
59                                                  size_t /* property_index */) const
60     {
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);
64     }
65
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
68     // being `null`).
69     //
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
74     {
75         return util::none;
76     }
77
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))
82             fn(v);
83     }
84
85     // Determine if `value` boxes the same List as `list`
86     bool is_same_list(List const& list, util::Any const& value)
87     {
88         if (auto list2 = any_cast<List>(&value))
89             return list == *list2;
90         return false;
91     }
92
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;
109
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
112     // implement this.
113     util::Any box(Mixed) const { REALM_TERMINATE("not supported"); }
114
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`.
118     //
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.
125     template<typename T>
126     T unbox(util::Any& v, bool /*create*/= false, bool /*update*/= false) const { return any_cast<T>(v); }
127
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 {}; }
131
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&) {}
135     void did_change() {}
136
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"; }
139
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; }
144
145 private:
146     std::shared_ptr<Realm> realm;
147     const ObjectSchema* object_schema = nullptr;
148
149 };
150
151 inline util::Any CppContext::box(RowExpr row) const
152 {
153     REALM_ASSERT(object_schema);
154     return Object(realm, *object_schema, row);
155 }
156
157 template<>
158 inline StringData CppContext::unbox(util::Any& v, bool, bool) const
159 {
160     if (!v.has_value())
161         return StringData();
162     auto& value = any_cast<std::string&>(v);
163     return StringData(value.c_str(), value.size());
164 }
165
166 template<>
167 inline BinaryData CppContext::unbox(util::Any& v, bool, bool) const
168 {
169     if (!v.has_value())
170         return BinaryData();
171     auto& value = any_cast<std::string&>(v);
172     return BinaryData(value.c_str(), value.size());
173 }
174
175 template<>
176 inline RowExpr CppContext::unbox(util::Any& v, bool create, bool update) const
177 {
178     if (auto object = any_cast<Object>(&v))
179         return object->row();
180     if (auto row = any_cast<RowExpr>(&v))
181         return *row;
182     if (!create)
183         return RowExpr();
184
185     REALM_ASSERT(object_schema);
186     return Object::create(const_cast<CppContext&>(*this), realm, *object_schema, v, update).row();
187 }
188
189 template<>
190 inline util::Optional<bool> CppContext::unbox(util::Any& v, bool, bool) const
191 {
192     return v.has_value() ? util::make_optional(unbox<bool>(v)) : util::none;
193 }
194
195 template<>
196 inline util::Optional<int64_t> CppContext::unbox(util::Any& v, bool, bool) const
197 {
198     return v.has_value() ? util::make_optional(unbox<int64_t>(v)) : util::none;
199 }
200
201 template<>
202 inline util::Optional<double> CppContext::unbox(util::Any& v, bool, bool) const
203 {
204     return v.has_value() ? util::make_optional(unbox<double>(v)) : util::none;
205 }
206
207 template<>
208 inline util::Optional<float> CppContext::unbox(util::Any& v, bool, bool) const
209 {
210     return v.has_value() ? util::make_optional(unbox<float>(v)) : util::none;
211 }
212
213 template<>
214 inline Mixed CppContext::unbox(util::Any&, bool, bool) const
215 {
216     throw std::logic_error("'Any' type is unsupported");
217 }
218 }
219
220 #endif // REALM_OS_OBJECT_ACCESSOR_IMPL_HPP