added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / RLMAccessor.mm
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2014 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 #import "RLMAccessor.hpp"
20
21 #import "RLMArray_Private.hpp"
22 #import "RLMListBase.h"
23 #import "RLMObjectSchema_Private.hpp"
24 #import "RLMObjectStore.h"
25 #import "RLMObject_Private.hpp"
26 #import "RLMObservation.hpp"
27 #import "RLMProperty_Private.h"
28 #import "RLMRealm_Private.hpp"
29 #import "RLMResults_Private.hpp"
30 #import "RLMSchema_Private.h"
31 #import "RLMUtil.hpp"
32 #import "results.hpp"
33 #import "property.hpp"
34
35 #import <objc/runtime.h>
36 #import <objc/message.h>
37 #import <realm/descriptor.hpp>
38
39 #pragma mark - Helper functions
40
41 namespace {
42 template<typename T>
43 T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
44     RLMVerifyAttached(obj);
45     return obj->_row.get<T>(obj->_info->objectSchema->persisted_properties[index].table_column);
46 }
47
48 template<typename T>
49 id getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) {
50     RLMVerifyAttached(obj);
51     auto& prop = obj->_info->objectSchema->persisted_properties[index];
52     auto col = prop.table_column;
53     if (obj->_row.is_null(col)) {
54         return nil;
55     }
56
57     RLMAccessorContext ctx(obj, &prop);
58     return ctx.box(obj->_row.get<T>(col));
59 }
60
61 template<typename T>
62 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, T val) {
63     RLMVerifyInWriteTransaction(obj);
64     obj->_row.set(colIndex, val);
65 }
66
67 template<typename Fn>
68 void translateError(Fn&& fn) {
69     try {
70         fn();
71     }
72     catch (std::exception const& e) {
73         @throw RLMException(e);
74     }
75 }
76
77 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
78               __unsafe_unretained NSString *const val) {
79     RLMVerifyInWriteTransaction(obj);
80     translateError([&] {
81         obj->_row.set(colIndex, RLMStringDataWithNSString(val));
82     });
83 }
84
85 [[gnu::noinline]]
86 void setNull(realm::Row& row, size_t col) {
87     translateError([&] { row.set_null(col); });
88 }
89
90 void setValue(__unsafe_unretained RLMObjectBase *const obj,
91               NSUInteger colIndex, __unsafe_unretained NSDate *const date) {
92     RLMVerifyInWriteTransaction(obj);
93     if (date) {
94         obj->_row.set(colIndex, RLMTimestampForNSDate(date));
95     }
96     else {
97         setNull(obj->_row, colIndex);
98     }
99 }
100
101 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
102               __unsafe_unretained NSData *const data) {
103     RLMVerifyInWriteTransaction(obj);
104     translateError([&] {
105         obj->_row.set(colIndex, RLMBinaryDataForNSData(data));
106     });
107 }
108
109 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
110               __unsafe_unretained RLMObjectBase *const val) {
111     if (!val) {
112         RLMVerifyInWriteTransaction(obj);
113         obj->_row.nullify_link(colIndex);
114         return;
115     }
116
117     RLMAddObjectToRealm(val, obj->_realm, false);
118
119     // make sure it is the correct type
120     if (val->_row.get_table() != obj->_row.get_table()->get_link_target(colIndex)) {
121         @throw RLMException(@"Can't set object of type '%@' to property of type '%@'",
122                             val->_objectSchema.className,
123                             obj->_info->propertyForTableColumn(colIndex).objectClassName);
124     }
125     obj->_row.set_link(colIndex, val->_row.get_index());
126 }
127
128 // array getter/setter
129 RLMArray *getArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger propIndex) {
130     RLMVerifyAttached(obj);
131     auto prop = obj->_info->rlmObjectSchema.properties[propIndex];
132     return [[RLMManagedArray alloc] initWithParent:obj property:prop];
133 }
134
135 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
136               __unsafe_unretained id<NSFastEnumeration> const value) {
137     RLMVerifyInWriteTransaction(obj);
138     auto prop = obj->_info->propertyForTableColumn(colIndex);
139     RLMValidateValueForProperty(value, obj->_info->rlmObjectSchema, prop, true);
140
141     realm::List list(obj->_realm->_realm, *obj->_row.get_table(), colIndex, obj->_row.get_index());
142     RLMClassInfo *info = obj->_info;
143     if (list.get_type() == realm::PropertyType::Object) {
144         info = &obj->_info->linkTargetType(prop.index);
145     }
146     RLMAccessorContext ctx(obj->_realm, *info);
147     translateError([&] { list.assign(ctx, value, false); });
148 }
149
150 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
151               __unsafe_unretained NSNumber<RLMInt> *const intObject) {
152     RLMVerifyInWriteTransaction(obj);
153
154     if (intObject) {
155         obj->_row.set(colIndex, intObject.longLongValue);
156     }
157     else {
158         setNull(obj->_row, colIndex);
159     }
160 }
161
162 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
163               __unsafe_unretained NSNumber<RLMFloat> *const floatObject) {
164     RLMVerifyInWriteTransaction(obj);
165
166     if (floatObject) {
167         obj->_row.set(colIndex, floatObject.floatValue);
168     }
169     else {
170         setNull(obj->_row, colIndex);
171     }
172 }
173
174 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
175               __unsafe_unretained NSNumber<RLMDouble> *const doubleObject) {
176     RLMVerifyInWriteTransaction(obj);
177
178     if (doubleObject) {
179         obj->_row.set(colIndex, doubleObject.doubleValue);
180     }
181     else {
182         setNull(obj->_row, colIndex);
183     }
184 }
185
186 void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex,
187               __unsafe_unretained NSNumber<RLMBool> *const boolObject) {
188     RLMVerifyInWriteTransaction(obj);
189
190     if (boolObject) {
191         obj->_row.set(colIndex, (bool)boolObject.boolValue);
192     }
193     else {
194         setNull(obj->_row, colIndex);
195     }
196 }
197
198 RLMLinkingObjects *getLinkingObjects(__unsafe_unretained RLMObjectBase *const obj,
199                                      __unsafe_unretained RLMProperty *const property) {
200     RLMVerifyAttached(obj);
201     auto& objectInfo = obj->_realm->_info[property.objectClassName];
202     auto linkingProperty = objectInfo.objectSchema->property_for_name(property.linkOriginPropertyName.UTF8String);
203     auto backlinkView = obj->_row.get_table()->get_backlink_view(obj->_row.get_index(), objectInfo.table(), linkingProperty->table_column);
204     realm::Results results(obj->_realm->_realm, std::move(backlinkView));
205     return [RLMLinkingObjects resultsWithObjectInfo:objectInfo results:std::move(results)];
206 }
207
208 // any getter/setter
209 template<typename Type, typename StorageType=Type>
210 id makeGetter(NSUInteger index) {
211     return ^(__unsafe_unretained RLMObjectBase *const obj) {
212         return static_cast<Type>(get<StorageType>(obj, index));
213     };
214 }
215
216 template<typename Type>
217 id makeBoxedGetter(NSUInteger index) {
218     return ^(__unsafe_unretained RLMObjectBase *const obj) {
219         return getBoxed<Type>(obj, index);
220     };
221 }
222 template<typename Type>
223 id makeOptionalGetter(NSUInteger index) {
224     return ^(__unsafe_unretained RLMObjectBase *const obj) {
225         return getBoxed<realm::util::Optional<Type>>(obj, index);
226     };
227 }
228 template<typename Type>
229 id makeNumberGetter(NSUInteger index, bool boxed, bool optional) {
230     if (optional) {
231         return makeOptionalGetter<Type>(index);
232     }
233     if (boxed) {
234         return makeBoxedGetter<Type>(index);
235     }
236     return makeGetter<Type>(index);
237 }
238
239 // dynamic getter with column closure
240 id managedGetter(RLMProperty *prop, const char *type) {
241     NSUInteger index = prop.index;
242     if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) {
243         return ^id(__unsafe_unretained RLMObjectBase *const obj) {
244             return getArray(obj, index);
245         };
246     }
247
248     bool boxed = *type == '@';
249     switch (prop.type) {
250         case RLMPropertyTypeInt:
251             if (prop.optional || boxed) {
252                 return makeNumberGetter<long long>(index, boxed, prop.optional);
253             }
254             switch (*type) {
255                 case 'c': return makeGetter<char, int64_t>(index);
256                 case 's': return makeGetter<short, int64_t>(index);
257                 case 'i': return makeGetter<int, int64_t>(index);
258                 case 'l': return makeGetter<long, int64_t>(index);
259                 case 'q': return makeGetter<long long, int64_t>(index);
260                 default:
261                     @throw RLMException(@"Unexpected property type for Objective-C type code");
262             }
263         case RLMPropertyTypeFloat:
264             return makeNumberGetter<float>(index, boxed, prop.optional);
265         case RLMPropertyTypeDouble:
266             return makeNumberGetter<double>(index, boxed, prop.optional);
267         case RLMPropertyTypeBool:
268             return makeNumberGetter<bool>(index, boxed, prop.optional);
269         case RLMPropertyTypeString:
270             return makeBoxedGetter<realm::StringData>(index);
271         case RLMPropertyTypeDate:
272             return makeBoxedGetter<realm::Timestamp>(index);
273         case RLMPropertyTypeData:
274             return makeBoxedGetter<realm::BinaryData>(index);
275         case RLMPropertyTypeObject:
276             return makeBoxedGetter<realm::RowExpr>(index);
277         case RLMPropertyTypeAny:
278             @throw RLMException(@"Cannot create accessor class for schema with Mixed properties");
279         case RLMPropertyTypeLinkingObjects:
280             return ^(__unsafe_unretained RLMObjectBase *const obj) {
281                 return getLinkingObjects(obj, prop);
282             };
283     }
284 }
285
286 template<typename ArgType, typename StorageType=ArgType>
287 id makeSetter(__unsafe_unretained RLMProperty *const prop) {
288     NSUInteger index = prop.index;
289     NSString *name = prop.name;
290     if (prop.isPrimary) {
291         return ^(__unused RLMObjectBase *obj, __unused ArgType val) {
292             @throw RLMException(@"Primary key can't be changed after an object is inserted.");
293         };
294     }
295
296     return ^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) {
297         auto set = [&] {
298             setValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column,
299                      static_cast<StorageType>(val));
300         };
301         if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo,
302                                                              obj->_row.get_index(), *obj->_info)) {
303             info->willChange(name);
304             set();
305             info->didChange(name);
306         }
307         else {
308             set();
309         }
310     };
311 }
312
313 // dynamic setter with column closure
314 id managedSetter(RLMProperty *prop, const char *type) {
315     if (prop.array && prop.type != RLMPropertyTypeLinkingObjects) {
316         return makeSetter<id<NSFastEnumeration>>(prop);
317     }
318
319     bool boxed = prop.optional || *type == '@';
320     switch (prop.type) {
321         case RLMPropertyTypeInt:
322             if (boxed) {
323                 return makeSetter<NSNumber<RLMInt> *>(prop);
324             }
325             switch (*type) {
326                 case 'c': return makeSetter<char, long long>(prop);
327                 case 's': return makeSetter<short, long long>(prop);
328                 case 'i': return makeSetter<int, long long>(prop);
329                 case 'l': return makeSetter<long, long long>(prop);
330                 case 'q': return makeSetter<long long>(prop);
331                 default:
332                     @throw RLMException(@"Unexpected property type for Objective-C type code");
333             }
334         case RLMPropertyTypeFloat:
335             return boxed ? makeSetter<NSNumber<RLMFloat> *>(prop) : makeSetter<float>(prop);
336         case RLMPropertyTypeDouble:
337             return boxed ? makeSetter<NSNumber<RLMDouble> *>(prop) : makeSetter<double>(prop);
338         case RLMPropertyTypeBool:
339             return boxed ? makeSetter<NSNumber<RLMBool> *>(prop) : makeSetter<BOOL, bool>(prop);
340         case RLMPropertyTypeString:         return makeSetter<NSString *>(prop);
341         case RLMPropertyTypeDate:           return makeSetter<NSDate *>(prop);
342         case RLMPropertyTypeData:           return makeSetter<NSData *>(prop);
343         case RLMPropertyTypeAny:            return nil;
344         case RLMPropertyTypeLinkingObjects: return nil;
345         case RLMPropertyTypeObject:         return makeSetter<RLMObjectBase *>(prop);
346     }
347 }
348
349 // call getter for superclass for property at colIndex
350 id superGet(RLMObjectBase *obj, NSString *propName) {
351     typedef id (*getter_type)(RLMObjectBase *, SEL);
352     RLMProperty *prop = obj->_objectSchema[propName];
353     Class superClass = class_getSuperclass(obj.class);
354     getter_type superGetter = (getter_type)[superClass instanceMethodForSelector:prop.getterSel];
355     return superGetter(obj, prop.getterSel);
356 }
357
358 // call setter for superclass for property at colIndex
359 void superSet(RLMObjectBase *obj, NSString *propName, id val) {
360     typedef void (*setter_type)(RLMObjectBase *, SEL, RLMArray *ar);
361     RLMProperty *prop = obj->_objectSchema[propName];
362     Class superClass = class_getSuperclass(obj.class);
363     setter_type superSetter = (setter_type)[superClass instanceMethodForSelector:prop.setterSel];
364     superSetter(obj, prop.setterSel, val);
365 }
366
367 // getter/setter for unmanaged object
368 id unmanagedGetter(RLMProperty *prop, const char *) {
369     // only override getters for RLMArray and linking objects properties
370     if (prop.type == RLMPropertyTypeLinkingObjects) {
371         return ^(RLMObjectBase *) { return [RLMResults emptyDetachedResults]; };
372     }
373     if (prop.array) {
374         NSString *propName = prop.name;
375         if (prop.type == RLMPropertyTypeObject) {
376             NSString *objectClassName = prop.objectClassName;
377             return ^(RLMObjectBase *obj) {
378                 id val = superGet(obj, propName);
379                 if (!val) {
380                     val = [[RLMArray alloc] initWithObjectClassName:objectClassName];
381                     superSet(obj, propName, val);
382                 }
383                 return val;
384             };
385         }
386         auto type = prop.type;
387         auto optional = prop.optional;
388         return ^(RLMObjectBase *obj) {
389             id val = superGet(obj, propName);
390             if (!val) {
391                 val = [[RLMArray alloc] initWithObjectType:type optional:optional];
392                 superSet(obj, propName, val);
393             }
394             return val;
395         };
396     }
397     return nil;
398 }
399
400 id unmanagedSetter(RLMProperty *prop, const char *) {
401     // Only RLMArray needs special handling for the unmanaged setter
402     if (!prop.array) {
403         return nil;
404     }
405
406     NSString *propName = prop.name;
407     return ^(RLMObjectBase *obj, id<NSFastEnumeration> values) {
408         auto prop = obj->_objectSchema[propName];
409         RLMValidateValueForProperty(values, obj->_objectSchema, prop, true);
410
411         // make copy when setting (as is the case for all other variants)
412         RLMArray *ar;
413         if (prop.type == RLMPropertyTypeObject)
414             ar = [[RLMArray alloc] initWithObjectClassName:prop.objectClassName];
415         else
416             ar = [[RLMArray alloc] initWithObjectType:prop.type optional:prop.optional];
417         [ar addObjects:values];
418         superSet(obj, propName, ar);
419     };
420 }
421
422 void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop,
423                id (*getter)(RLMProperty *, const char *),
424                id (*setter)(RLMProperty *, const char *)) {
425     SEL sel = prop.getterSel;
426     auto getterMethod = class_getInstanceMethod(cls, sel);
427     if (!getterMethod) {
428         return;
429     }
430
431     const char *getterType = method_getTypeEncoding(getterMethod);
432     if (id block = getter(prop, getterType)) {
433         class_addMethod(cls, sel, imp_implementationWithBlock(block), getterType);
434     }
435
436     if (!(sel = prop.setterSel)) {
437         return;
438     }
439     auto setterMethod = class_getInstanceMethod(cls, sel);
440     if (!setterMethod) {
441         return;
442     }
443     if (id block = setter(prop, getterType)) { // note: deliberately getterType as it's easier to grab the relevant type from
444         class_addMethod(cls, sel, imp_implementationWithBlock(block), method_getTypeEncoding(setterMethod));
445     }
446 }
447
448 Class createAccessorClass(Class objectClass,
449                           RLMObjectSchema *schema,
450                           const char *accessorClassName,
451                           id (*getterGetter)(RLMProperty *, const char *),
452                           id (*setterGetter)(RLMProperty *, const char *)) {
453     REALM_ASSERT_DEBUG(RLMIsObjectOrSubclass(objectClass));
454
455     // create and register proxy class which derives from object class
456     Class accClass = objc_allocateClassPair(objectClass, accessorClassName, 0);
457     if (!accClass) {
458         // Class with that name already exists, so just return the pre-existing one
459         // This should only happen for our standalone "accessors"
460         return objc_lookUpClass(accessorClassName);
461     }
462
463     // override getters/setters for each propery
464     for (RLMProperty *prop in schema.properties) {
465         addMethod(accClass, prop, getterGetter, setterGetter);
466     }
467     for (RLMProperty *prop in schema.computedProperties) {
468         addMethod(accClass, prop, getterGetter, setterGetter);
469     }
470
471     objc_registerClassPair(accClass);
472
473     return accClass;
474 }
475 } // anonymous namespace
476
477 #pragma mark - Public Interface
478
479 Class RLMManagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, const char *name) {
480     return createAccessorClass(objectClass, schema, name, managedGetter, managedSetter);
481 }
482
483 Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema) {
484     return createAccessorClass(objectClass, schema,
485                                [@"RLM:Unmanaged " stringByAppendingString:schema.className].UTF8String,
486                                unmanagedGetter, unmanagedSetter);
487 }
488
489 // implement the class method className on accessors to return the className of the
490 // base object
491 void RLMReplaceClassNameMethod(Class accessorClass, NSString *className) {
492     Class metaClass = object_getClass(accessorClass);
493     IMP imp = imp_implementationWithBlock(^(Class){ return className; });
494     class_addMethod(metaClass, @selector(className), imp, "@@:");
495 }
496
497 // implement the shared schema method
498 void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema *schema) {
499     Class metaClass = object_getClass(accessorClass);
500     IMP imp = imp_implementationWithBlock(^(Class cls) {
501         if (cls == accessorClass) {
502             return schema;
503         }
504
505         // If we aren't being called directly on the class this was overriden
506         // for, the class is either a subclass which we haven't initialized yet,
507         // or it's a runtime-generated class which should use the parent's
508         // schema. We check for the latter by checking if the immediate
509         // descendent of the desired class is a class generated by us (there
510         // may be further subclasses not generated by us for things like KVO).
511         Class parent = class_getSuperclass(cls);
512         while (parent != accessorClass) {
513             cls = parent;
514             parent = class_getSuperclass(cls);
515         }
516
517         static const char accessorClassPrefix[] = "RLM:";
518         if (!strncmp(class_getName(cls), accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) {
519             return schema;
520         }
521
522         return [RLMSchema sharedSchemaForClass:cls];
523     });
524     class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:");
525 }
526
527 void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id val) {
528     RLMObjectSchema *schema = obj->_objectSchema;
529     RLMProperty *prop = schema[propName];
530     if (!prop) {
531         @throw RLMException(@"Invalid property name '%@' for class '%@'.",
532                             propName, obj->_objectSchema.className);
533     }
534     if (prop.isPrimary) {
535         @throw RLMException(@"Primary key can't be changed to '%@' after an object is inserted.", val);
536     }
537     RLMValidateValueForProperty(val, schema, prop, true);
538     RLMDynamicSet(obj, prop, RLMCoerceToNil(val));
539 }
540
541 // Precondition: the property is not a primary key
542 void RLMDynamicSet(__unsafe_unretained RLMObjectBase *const obj,
543                    __unsafe_unretained RLMProperty *const prop,
544                    __unsafe_unretained id const val) {
545     REALM_ASSERT_DEBUG(!prop.isPrimary);
546     realm::Object o(obj->_info->realm->_realm, *obj->_info->objectSchema, obj->_row);
547     RLMAccessorContext c(obj);
548     translateError([&] {
549         o.set_property_value(c, prop.name.UTF8String, val ?: NSNull.null, false);
550     });
551 }
552
553 id RLMDynamicGet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop) {
554     realm::Object o(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row);
555     RLMAccessorContext c(obj);
556     c.currentProperty = prop;
557     return RLMCoerceToNil(o.get_property_value<id>(c, prop.name.UTF8String));
558 }
559
560 id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj,
561                        __unsafe_unretained NSString *const propName, bool asList) {
562     RLMProperty *prop = obj->_objectSchema[propName];
563     if (!prop) {
564         @throw RLMException(@"Invalid property name '%@' for class '%@'.",
565                             propName, obj->_objectSchema.className);
566     }
567     if (asList && prop.array && prop.swiftIvar) {
568         RLMListBase *list = object_getIvar(obj, prop.swiftIvar);
569         if (prop.type != RLMPropertyTypeLinkingObjects && !list._rlmArray) {
570             list._rlmArray = RLMDynamicGet(obj, prop);
571         }
572         return list;
573     }
574
575     return RLMDynamicGet(obj, prop);
576 }
577
578 RLMAccessorContext::RLMAccessorContext(RLMAccessorContext& parent, realm::Property const& property)
579 : _realm(parent._realm)
580 , _info(property.type == realm::PropertyType::Object ? parent._info.linkTargetType(property) : parent._info)
581 , _promote_existing(parent._promote_existing)
582 {
583 }
584
585 RLMAccessorContext::RLMAccessorContext(RLMRealm *realm, RLMClassInfo& info, bool promote)
586 : _realm(realm), _info(info), _promote_existing(promote)
587 {
588 }
589
590 RLMAccessorContext::RLMAccessorContext(__unsafe_unretained RLMObjectBase *const parent,
591                                        const realm::Property *prop)
592 : _realm(parent->_realm)
593 , _info(prop && prop->type == realm::PropertyType::Object ? parent->_info->linkTargetType(*prop)
594                                                           : *parent->_info)
595 , _parentObject(parent)
596 {
597 }
598
599 id RLMAccessorContext::defaultValue(__unsafe_unretained NSString *const key) {
600     if (!_defaultValues) {
601         _defaultValues = RLMDefaultValuesForObjectSchema(_info.rlmObjectSchema);
602     }
603     return _defaultValues[key];
604 }
605
606 id RLMAccessorContext::propertyValue(__unsafe_unretained id const obj, size_t propIndex,
607                                      __unsafe_unretained RLMProperty *const prop) {
608     // Property value from an NSArray
609     if ([obj respondsToSelector:@selector(objectAtIndex:)]) {
610         return propIndex < [obj count] ? [obj objectAtIndex:propIndex] : nil;
611     }
612
613     // Property value from an NSDictionary
614     if ([obj respondsToSelector:@selector(objectForKey:)]) {
615         return [obj objectForKey:prop.name];
616     }
617
618     // Property value from an instance of this object type
619     id value;
620     if ([obj isKindOfClass:_info.rlmObjectSchema.objectClass] && prop.swiftIvar) {
621         if (prop.array) {
622             return static_cast<RLMListBase *>(object_getIvar(obj, prop.swiftIvar))._rlmArray;
623         }
624         else { // optional
625             value = static_cast<RLMOptionalBase *>(object_getIvar(obj, prop.swiftIvar)).underlyingValue;
626         }
627     }
628     else {
629     // Property value from some object that's KVC-compatible
630         value = RLMValidatedValueForProperty(obj, [obj respondsToSelector:prop.getterSel] ? prop.getterName : prop.name,
631                                              _info.rlmObjectSchema.className);
632     }
633     return value ?: NSNull.null;
634 }
635
636 id RLMAccessorContext::box(realm::List&& l) {
637     REALM_ASSERT(_parentObject);
638     REALM_ASSERT(currentProperty);
639     return [[RLMManagedArray alloc] initWithList:std::move(l) realm:_realm
640                                       parentInfo:_parentObject->_info
641                                         property:currentProperty];
642 }
643
644 id RLMAccessorContext::box(realm::Object&& o) {
645     REALM_ASSERT(currentProperty);
646     return RLMCreateObjectAccessor(_realm, _info.linkTargetType(currentProperty.index), o.row());
647 }
648
649 id RLMAccessorContext::box(realm::RowExpr r) {
650     return RLMCreateObjectAccessor(_realm, _info, r);
651 }
652
653 id RLMAccessorContext::box(realm::Results&& r) {
654     REALM_ASSERT(currentProperty);
655     return [RLMResults resultsWithObjectInfo:_realm->_info[currentProperty.objectClassName]
656                                      results:std::move(r)];
657 }
658
659 template<>
660 realm::Timestamp RLMAccessorContext::unbox(__unsafe_unretained id const value, bool, bool) {
661     id v = RLMCoerceToNil(value);
662     return RLMTimestampForNSDate(v);
663 }
664
665 template<>
666 bool RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
667     return [v boolValue];
668 }
669 template<>
670 double RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
671     return [v doubleValue];
672 }
673 template<>
674 float RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
675     return [v floatValue];
676 }
677 template<>
678 long long RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
679     return [v longLongValue];
680 }
681 template<>
682 realm::BinaryData RLMAccessorContext::unbox(id v, bool, bool) {
683     v = RLMCoerceToNil(v);
684     return RLMBinaryDataForNSData(v);
685 }
686 template<>
687 realm::StringData RLMAccessorContext::unbox(id v, bool, bool) {
688     v = RLMCoerceToNil(v);
689     return RLMStringDataWithNSString(v);
690 }
691
692 template<typename Fn>
693 static auto to_optional(__unsafe_unretained id const value, Fn&& fn) {
694     id v = RLMCoerceToNil(value);
695     return v && v != NSNull.null ? realm::util::make_optional(fn(v)) : realm::util::none;
696 }
697
698 template<>
699 realm::util::Optional<bool> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
700     return to_optional(v, [&](__unsafe_unretained id v) { return (bool)[v boolValue]; });
701 }
702 template<>
703 realm::util::Optional<double> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
704     return to_optional(v, [&](__unsafe_unretained id v) { return [v doubleValue]; });
705 }
706 template<>
707 realm::util::Optional<float> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
708     return to_optional(v, [&](__unsafe_unretained id v) { return [v floatValue]; });
709 }
710 template<>
711 realm::util::Optional<int64_t> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
712     return to_optional(v, [&](__unsafe_unretained id v) { return [v longLongValue]; });
713 }
714
715 template<>
716 realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, bool create, bool update) {
717     RLMObjectBase *link = RLMDynamicCast<RLMObjectBase>(v);
718     if (!link) {
719         if (!create)
720             return realm::RowExpr();
721         return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, update)->_row;
722     }
723
724     if (link.isInvalidated) {
725         if (create) {
726             @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted");
727         }
728         else {
729             @throw RLMException(@"Object has been invalidated");
730         }
731     }
732
733     if (![link->_objectSchema.className isEqualToString:_info.rlmObjectSchema.className]) {
734         if (create && !_promote_existing)
735             return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, update)->_row;
736         return link->_row;
737     }
738
739     if (!link->_realm) {
740         if (!create)
741             return realm::RowExpr();
742         if (!_promote_existing)
743             return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, update)->_row;
744         RLMAddObjectToRealm(link, _realm, update);
745     }
746     else if (link->_realm != _realm) {
747         if (_promote_existing)
748             @throw RLMException(@"Object is already managed by another Realm. Use create instead to copy it into this Realm.");
749         return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, update)->_row;
750     }
751     return link->_row;
752 }
753
754 void RLMAccessorContext::will_change(realm::Row const& row, realm::Property const& prop) {
755     _observationInfo = RLMGetObservationInfo(nullptr, row.get_index(), _info);
756     if (_observationInfo) {
757         _kvoPropertyName = @(prop.name.c_str());
758         _observationInfo->willChange(_kvoPropertyName);
759     }
760 }
761
762 void RLMAccessorContext::did_change() {
763     if (_observationInfo) {
764         _observationInfo->didChange(_kvoPropertyName);
765         _kvoPropertyName = nil;
766         _observationInfo = nullptr;
767     }
768 }
769
770 RLMOptionalId RLMAccessorContext::value_for_property(__unsafe_unretained id const obj,
771                                                      std::string const&, size_t propIndex) {
772     auto prop = _info.rlmObjectSchema.properties[propIndex];
773     id value = propertyValue(obj, propIndex, prop);
774     if (value) {
775         RLMValidateValueForProperty(value, _info.rlmObjectSchema, prop);
776     }
777
778     if (_promote_existing && [obj isKindOfClass:_info.rlmObjectSchema.objectClass] && !prop.swiftIvar) {
779         // set the ivars for object and array properties to nil as otherwise the
780         // accessors retain objects that are no longer accessible via the properties
781         // this is mainly an issue when the object graph being added has cycles,
782         // as it's not obvious that the user has to set the *ivars* to nil to
783         // avoid leaking memory
784         if (prop.type == RLMPropertyTypeObject) {
785             ((void(*)(id, SEL, id))objc_msgSend)(obj, prop.setterSel, nil);
786         }
787     }
788
789     return RLMOptionalId{value};
790 }
791
792 RLMOptionalId RLMAccessorContext::default_value_for_property(realm::ObjectSchema const&,
793                                                              std::string const& prop)
794 {
795     return RLMOptionalId{defaultValue(@(prop.c_str()))};
796 }
797
798 bool RLMAccessorContext::is_same_list(realm::List const& list, __unsafe_unretained id const v) const noexcept {
799     return [v respondsToSelector:@selector(isBackedByList:)] && [v isBackedByList:list];
800 }