added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / ObjectStore / src / object_schema.cpp
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2015 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 #include "object_schema.hpp"
20
21 #include "feature_checks.hpp"
22 #include "object_store.hpp"
23 #include "property.hpp"
24 #include "schema.hpp"
25
26
27 #include <realm/data_type.hpp>
28 #include <realm/descriptor.hpp>
29 #include <realm/group.hpp>
30 #include <realm/table.hpp>
31
32 using namespace realm;
33
34 ObjectSchema::ObjectSchema() = default;
35 ObjectSchema::~ObjectSchema() = default;
36
37 ObjectSchema::ObjectSchema(std::string name, std::initializer_list<Property> persisted_properties)
38 : ObjectSchema(std::move(name), persisted_properties, {})
39 {
40 }
41
42 ObjectSchema::ObjectSchema(std::string name, std::initializer_list<Property> persisted_properties,
43                            std::initializer_list<Property> computed_properties)
44 : name(std::move(name))
45 , persisted_properties(persisted_properties)
46 , computed_properties(computed_properties)
47 {
48     for (auto const& prop : persisted_properties) {
49         if (prop.is_primary) {
50             primary_key = prop.name;
51             break;
52         }
53     }
54 }
55
56 PropertyType ObjectSchema::from_core_type(Descriptor const& table, size_t col)
57 {
58     auto optional = table.is_nullable(col) ? PropertyType::Nullable : PropertyType::Required;
59     switch (table.get_column_type(col)) {
60         case type_Int:       return PropertyType::Int | optional;
61         case type_Float:     return PropertyType::Float | optional;
62         case type_Double:    return PropertyType::Double | optional;
63         case type_Bool:      return PropertyType::Bool | optional;
64         case type_String:    return PropertyType::String | optional;
65         case type_Binary:    return PropertyType::Data | optional;
66         case type_Timestamp: return PropertyType::Date | optional;
67         case type_Mixed:     return PropertyType::Any | optional;
68         case type_Link:      return PropertyType::Object | PropertyType::Nullable;
69         case type_LinkList:  return PropertyType::Object | PropertyType::Array;
70         case type_Table:     return from_core_type(*table.get_subdescriptor(col), 0) | PropertyType::Array;
71         default: REALM_UNREACHABLE();
72     }
73 }
74
75 ObjectSchema::ObjectSchema(Group const& group, StringData name, size_t index) : name(name) {
76     ConstTableRef table;
77     if (index < group.size()) {
78         table = group.get_table(index);
79     }
80     else {
81         table = ObjectStore::table_for_object_type(group, name);
82     }
83
84     size_t count = table->get_column_count();
85     persisted_properties.reserve(count);
86     for (size_t col = 0; col < count; col++) {
87         if (auto property = ObjectStore::property_for_column_index(table, col)) {
88             persisted_properties.push_back(std::move(property.value()));
89         }
90     }
91
92     primary_key = realm::ObjectStore::get_primary_key_for_object(group, name);
93     set_primary_key_property();
94 }
95
96 Property *ObjectSchema::property_for_name(StringData name) {
97     for (auto& prop : persisted_properties) {
98         if (StringData(prop.name) == name) {
99             return &prop;
100         }
101     }
102     for (auto& prop : computed_properties) {
103         if (StringData(prop.name) == name) {
104             return &prop;
105         }
106     }
107     return nullptr;
108 }
109
110 const Property *ObjectSchema::property_for_name(StringData name) const {
111     return const_cast<ObjectSchema *>(this)->property_for_name(name);
112 }
113
114 bool ObjectSchema::property_is_computed(Property const& property) const {
115     auto end = computed_properties.end();
116     return std::find(computed_properties.begin(), end, property) != end;
117 }
118
119 void ObjectSchema::set_primary_key_property()
120 {
121     if (primary_key.length()) {
122         if (auto primary_key_prop = primary_key_property()) {
123             primary_key_prop->is_primary = true;
124         }
125     }
126 }
127
128 static void validate_property(Schema const& schema,
129                               std::string const& object_name,
130                               Property const& prop,
131                               Property const** primary,
132                               std::vector<ObjectSchemaValidationException>& exceptions)
133 {
134     if (prop.type == PropertyType::LinkingObjects && !is_array(prop.type)) {
135         exceptions.emplace_back("Linking Objects property '%1.%2' must be an array.",
136                                 object_name, prop.name);
137     }
138
139     // check nullablity
140     if (is_nullable(prop.type) && !prop.type_is_nullable()) {
141         exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be nullable.",
142                                 object_name, prop.name, string_for_property_type(prop.type));
143     }
144     else if (prop.type == PropertyType::Object && !is_nullable(prop.type) && !is_array(prop.type)) {
145         exceptions.emplace_back("Property '%1.%2' of type 'object' must be nullable.", object_name, prop.name);
146     }
147
148     // check primary keys
149     if (prop.is_primary) {
150         if (prop.type != PropertyType::Int && prop.type != PropertyType::String) {
151             exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be made the primary key.",
152                                     object_name, prop.name, string_for_property_type(prop.type));
153         }
154         if (*primary) {
155             exceptions.emplace_back("Properties '%1' and '%2' are both marked as the primary key of '%3'.",
156                                     prop.name, (*primary)->name, object_name);
157         }
158         *primary = &prop;
159     }
160
161     // check indexable
162     if (prop.is_indexed && !prop.type_is_indexable()) {
163         exceptions.emplace_back("Property '%1.%2' of type '%3' cannot be indexed.",
164                                 object_name, prop.name, string_for_property_type(prop.type));
165     }
166
167     // check that only link properties have object types
168     if (prop.type != PropertyType::LinkingObjects && !prop.link_origin_property_name.empty()) {
169         exceptions.emplace_back("Property '%1.%2' of type '%3' cannot have an origin property name.",
170                                 object_name, prop.name, string_for_property_type(prop.type));
171     }
172     else if (prop.type == PropertyType::LinkingObjects && prop.link_origin_property_name.empty()) {
173         exceptions.emplace_back("Property '%1.%2' of type '%3' must have an origin property name.",
174                                 object_name, prop.name, string_for_property_type(prop.type));
175     }
176
177     if (prop.type != PropertyType::Object && prop.type != PropertyType::LinkingObjects) {
178         if (!prop.object_type.empty()) {
179             exceptions.emplace_back("Property '%1.%2' of type '%3' cannot have an object type.",
180                                     object_name, prop.name, prop.type_string());
181         }
182         return;
183     }
184
185
186     // check that the object_type is valid for link properties
187     auto it = schema.find(prop.object_type);
188     if (it == schema.end()) {
189         exceptions.emplace_back("Property '%1.%2' of type '%3' has unknown object type '%4'",
190                                 object_name, prop.name, string_for_property_type(prop.type), prop.object_type);
191         return;
192     }
193     if (prop.type != PropertyType::LinkingObjects) {
194         return;
195     }
196
197     const Property *origin_property = it->property_for_name(prop.link_origin_property_name);
198     if (!origin_property) {
199         exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' does not exist",
200                                 prop.object_type, prop.link_origin_property_name,
201                                 object_name, prop.name);
202     }
203     else if (origin_property->type != PropertyType::Object) {
204         exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' is not a link",
205                                 prop.object_type, prop.link_origin_property_name,
206                                 object_name, prop.name);
207     }
208     else if (origin_property->object_type != object_name) {
209         exceptions.emplace_back("Property '%1.%2' declared as origin of linking objects property '%3.%4' links to type '%5'",
210                                 prop.object_type, prop.link_origin_property_name,
211                                 object_name, prop.name, origin_property->object_type);
212     }
213 }
214
215 void ObjectSchema::validate(Schema const& schema, std::vector<ObjectSchemaValidationException>& exceptions) const
216 {
217     const Property *primary = nullptr;
218     for (auto const& prop : persisted_properties) {
219         validate_property(schema, name, prop, &primary, exceptions);
220     }
221     for (auto const& prop : computed_properties) {
222         validate_property(schema, name, prop, &primary, exceptions);
223     }
224
225     if (!primary_key.empty() && !primary && !primary_key_property()) {
226         exceptions.emplace_back("Specified primary key '%1.%2' does not exist.", name, primary_key);
227     }
228 }
229
230 namespace realm {
231 bool operator==(ObjectSchema const& a, ObjectSchema const& b)
232 {
233     return std::tie(a.name, a.primary_key, a.persisted_properties, a.computed_properties)
234         == std::tie(b.name, b.primary_key, b.persisted_properties, b.computed_properties);
235
236 }
237 }