added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / RLMObjectBase.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 "RLMObject_Private.hpp"
20
21 #import "RLMAccessor.h"
22 #import "RLMArray_Private.hpp"
23 #import "RLMListBase.h"
24 #import "RLMObjectSchema_Private.hpp"
25 #import "RLMObjectStore.h"
26 #import "RLMObservation.hpp"
27 #import "RLMOptionalBase.h"
28 #import "RLMProperty_Private.h"
29 #import "RLMRealm_Private.hpp"
30 #import "RLMSchema_Private.h"
31 #import "RLMSwiftSupport.h"
32 #import "RLMThreadSafeReference_Private.hpp"
33 #import "RLMUtil.hpp"
34
35 #import "object.hpp"
36 #import "shared_realm.hpp"
37
38 using namespace realm;
39
40 const NSUInteger RLMDescriptionMaxDepth = 5;
41
42 static bool maybeInitObjectSchemaForUnmanaged(RLMObjectBase *obj) {
43     obj->_objectSchema = [obj.class sharedSchema];
44     if (!obj->_objectSchema) {
45         return false;
46     }
47
48     // set default values
49     if (!obj->_objectSchema.isSwiftClass) {
50         NSDictionary *dict = RLMDefaultValuesForObjectSchema(obj->_objectSchema);
51         for (NSString *key in dict) {
52             [obj setValue:dict[key] forKey:key];
53         }
54     }
55
56     // set unmanaged accessor class
57     object_setClass(obj, obj->_objectSchema.unmanagedClass);
58     return true;
59 }
60
61 @interface RLMObjectBase () <RLMThreadConfined, RLMThreadConfined_Private>
62 @end
63
64 @implementation RLMObjectBase
65 // unmanaged init
66 - (instancetype)init {
67     if ((self = [super init])) {
68         maybeInitObjectSchemaForUnmanaged(self);
69     }
70     return self;
71 }
72
73 - (void)dealloc {
74     // This can't be a unique_ptr because associated objects are removed
75     // *after* c++ members are destroyed and dealloc is called, and we need it
76     // to be in a validish state when that happens
77     delete _observationInfo;
78     _observationInfo = nullptr;
79 }
80
81 static id coerceToObjectType(id obj, Class cls, RLMSchema *schema) {
82     return [obj isKindOfClass:cls] ? obj : [[cls alloc] initWithValue:obj schema:schema];
83 }
84
85 static id validatedObjectForProperty(__unsafe_unretained id const obj,
86                                      __unsafe_unretained RLMObjectSchema *const objectSchema,
87                                      __unsafe_unretained RLMProperty *const prop,
88                                      __unsafe_unretained RLMSchema *const schema) {
89     RLMValidateValueForProperty(obj, objectSchema, prop);
90     if (!obj || obj == NSNull.null) {
91         return nil;
92     }
93     if (prop.type == RLMPropertyTypeObject) {
94         Class objectClass = schema[prop.objectClassName].objectClass;
95         if (prop.array) {
96             NSMutableArray *ret = [[NSMutableArray alloc] init];
97             for (id el in obj) {
98                 [ret addObject:coerceToObjectType(el, objectClass, schema)];
99             }
100             return ret;
101         }
102         return coerceToObjectType(obj, objectClass, schema);
103     }
104     return obj;
105 }
106
107 - (instancetype)initWithValue:(id)value schema:(RLMSchema *)schema {
108     if (!(self = [super init])) {
109         return self;
110     }
111
112     if (!value || value == NSNull.null) {
113         @throw RLMException(@"Must provide a non-nil value.");
114     }
115
116     if (!maybeInitObjectSchemaForUnmanaged(self)) {
117         // Don't populate fields from the passed-in object if we're called
118         // during schema init
119         return self;
120     }
121
122     NSArray *properties = _objectSchema.properties;
123     if (NSArray *array = RLMDynamicCast<NSArray>(value)) {
124         if (array.count > properties.count) {
125             @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).",
126                                 (unsigned long long)array.count, (unsigned long long)properties.count);
127         }
128         NSUInteger i = 0;
129         for (id val in array) {
130             RLMProperty *prop = properties[i++];
131             [self setValue:validatedObjectForProperty(RLMCoerceToNil(val), _objectSchema, prop, schema)
132                     forKey:prop.name];
133         }
134     }
135     else {
136         // assume our object is an NSDictionary or an object with kvc properties
137         for (RLMProperty *prop in properties) {
138             id obj = RLMValidatedValueForProperty(value, prop.name, _objectSchema.className);
139
140             // don't set unspecified properties
141             if (!obj) {
142                 continue;
143             }
144
145             [self setValue:validatedObjectForProperty(RLMCoerceToNil(obj), _objectSchema, prop, schema)
146                     forKey:prop.name];
147         }
148     }
149
150     return self;
151 }
152
153 id RLMCreateManagedAccessor(Class cls, __unsafe_unretained RLMRealm *realm, RLMClassInfo *info) {
154     RLMObjectBase *obj = [[cls alloc] initWithRealm:realm schema:info->rlmObjectSchema];
155     obj->_info = info;
156     return obj;
157 }
158
159 - (instancetype)initWithRealm:(__unsafe_unretained RLMRealm *const)realm
160                        schema:(RLMObjectSchema *)schema {
161     self = [super init];
162     if (self) {
163         _realm = realm;
164         _objectSchema = schema;
165     }
166     return self;
167 }
168
169 - (id)valueForKey:(NSString *)key {
170     if (_observationInfo) {
171         return _observationInfo->valueForKey(key);
172     }
173     return [super valueForKey:key];
174 }
175
176 // Generic Swift properties can't be dynamic, so KVO doesn't work for them by default
177 - (id)valueForUndefinedKey:(NSString *)key {
178     if (Ivar ivar = _objectSchema[key].swiftIvar) {
179         return RLMCoerceToNil(object_getIvar(self, ivar));
180     }
181     return [super valueForUndefinedKey:key];
182 }
183
184 - (void)setValue:(id)value forUndefinedKey:(NSString *)key {
185     value = RLMCoerceToNil(value);
186     RLMProperty *property = _objectSchema[key];
187     if (Ivar ivar = property.swiftIvar) {
188         if (property.array && (!value || [value conformsToProtocol:@protocol(NSFastEnumeration)])) {
189             RLMArray *array = [object_getIvar(self, ivar) _rlmArray];
190             [array removeAllObjects];
191
192             if (value) {
193                 [array addObjects:validatedObjectForProperty(value, _objectSchema, property,
194                                                              RLMSchema.partialPrivateSharedSchema)];
195             }
196         }
197         else if (property.optional) {
198             RLMOptionalBase *optional = object_getIvar(self, ivar);
199             optional.underlyingValue = value;
200         }
201         return;
202     }
203     [super setValue:value forUndefinedKey:key];
204 }
205
206 // overridden at runtime per-class for performance
207 + (NSString *)className {
208     NSString *className = NSStringFromClass(self);
209     if ([RLMSwiftSupport isSwiftClassName:className]) {
210         className = [RLMSwiftSupport demangleClassName:className];
211     }
212     return className;
213 }
214
215 // overridden at runtime per-class for performance
216 + (RLMObjectSchema *)sharedSchema {
217     return [RLMSchema sharedSchemaForClass:self.class];
218 }
219
220 + (void)initializeLinkedObjectSchemas {
221     for (RLMProperty *prop in self.sharedSchema.properties) {
222         if (prop.type == RLMPropertyTypeObject && !RLMSchema.partialPrivateSharedSchema[prop.objectClassName]) {
223             [[RLMSchema classForString:prop.objectClassName] initializeLinkedObjectSchemas];
224         }
225     }
226 }
227
228 + (Class)objectUtilClass:(BOOL)isSwift {
229     return RLMObjectUtilClass(isSwift);
230 }
231
232 - (NSString *)description
233 {
234     if (self.isInvalidated) {
235         return @"[invalid object]";
236     }
237
238     return [self descriptionWithMaxDepth:RLMDescriptionMaxDepth];
239 }
240
241 - (NSString *)descriptionWithMaxDepth:(NSUInteger)depth {
242     if (depth == 0) {
243         return @"<Maximum depth exceeded>";
244     }
245
246     NSString *baseClassName = _objectSchema.className;
247     NSMutableString *mString = [NSMutableString stringWithFormat:@"%@ {\n", baseClassName];
248
249     for (RLMProperty *property in _objectSchema.properties) {
250         id object = RLMObjectBaseObjectForKeyedSubscript(self, property.name);
251         NSString *sub;
252         if ([object respondsToSelector:@selector(descriptionWithMaxDepth:)]) {
253             sub = [object descriptionWithMaxDepth:depth - 1];
254         }
255         else if (property.type == RLMPropertyTypeData) {
256             static NSUInteger maxPrintedDataLength = 24;
257             NSData *data = object;
258             NSUInteger length = data.length;
259             if (length > maxPrintedDataLength) {
260                 data = [NSData dataWithBytes:data.bytes length:maxPrintedDataLength];
261             }
262             NSString *dataDescription = [data description];
263             sub = [NSString stringWithFormat:@"<%@ — %lu total bytes>", [dataDescription substringWithRange:NSMakeRange(1, dataDescription.length - 2)], (unsigned long)length];
264         }
265         else {
266             sub = [object description];
267         }
268         [mString appendFormat:@"\t%@ = %@;\n", property.name, [sub stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]];
269     }
270     [mString appendString:@"}"];
271
272     return [NSString stringWithString:mString];
273 }
274
275 - (RLMRealm *)realm {
276     return _realm;
277 }
278
279 - (RLMObjectSchema *)objectSchema {
280     return _objectSchema;
281 }
282
283 - (BOOL)isInvalidated {
284     // if not unmanaged and our accessor has been detached, we have been deleted
285     return self.class == _objectSchema.accessorClass && !_row.is_attached();
286 }
287
288 - (BOOL)isEqual:(id)object {
289     if (RLMObjectBase *other = RLMDynamicCast<RLMObjectBase>(object)) {
290         if (_objectSchema.primaryKeyProperty) {
291             return RLMObjectBaseAreEqual(self, other);
292         }
293     }
294     return [super isEqual:object];
295 }
296
297 - (NSUInteger)hash {
298     if (_objectSchema.primaryKeyProperty) {
299         id primaryProperty = [self valueForKey:_objectSchema.primaryKeyProperty.name];
300
301         // modify the hash of our primary key value to avoid potential (although unlikely) collisions
302         return [primaryProperty hash] ^ 1;
303     }
304     else {
305         return [super hash];
306     }
307 }
308
309 + (BOOL)shouldIncludeInDefaultSchema {
310     return RLMIsObjectSubclass(self);
311 }
312
313 + (NSString *)_realmObjectName {
314     return nil;
315 }
316
317 - (id)mutableArrayValueForKey:(NSString *)key {
318     id obj = [self valueForKey:key];
319     if ([obj isKindOfClass:[RLMArray class]]) {
320         return obj;
321     }
322     return [super mutableArrayValueForKey:key];
323 }
324
325 - (void)addObserver:(id)observer
326          forKeyPath:(NSString *)keyPath
327             options:(NSKeyValueObservingOptions)options
328             context:(void *)context {
329     if (!_observationInfo) {
330         _observationInfo = new RLMObservationInfo(self);
331     }
332     _observationInfo->recordObserver(_row, _info, _objectSchema, keyPath);
333
334     [super addObserver:observer forKeyPath:keyPath options:options context:context];
335 }
336
337 - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
338     [super removeObserver:observer forKeyPath:keyPath];
339     if (_observationInfo)
340         _observationInfo->removeObserver();
341 }
342
343 + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
344     const char *className = class_getName(self);
345     const char accessorClassPrefix[] = "RLM:Managed";
346     if (!strncmp(className, accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) {
347         if ([class_getSuperclass(self.class) sharedSchema][key]) {
348             return NO;
349         }
350     }
351
352     return [super automaticallyNotifiesObserversForKey:key];
353 }
354
355 #pragma mark - Thread Confined Protocol Conformance
356
357 - (std::unique_ptr<realm::ThreadSafeReferenceBase>)makeThreadSafeReference {
358     Object object(_realm->_realm, *_info->objectSchema, _row);
359     realm::ThreadSafeReference<Object> reference = _realm->_realm->obtain_thread_safe_reference(std::move(object));
360     return std::make_unique<realm::ThreadSafeReference<Object>>(std::move(reference));
361 }
362
363 - (id)objectiveCMetadata {
364     return nil;
365 }
366
367 + (instancetype)objectWithThreadSafeReference:(std::unique_ptr<realm::ThreadSafeReferenceBase>)reference
368                                      metadata:(__unused id)metadata
369                                         realm:(RLMRealm *)realm {
370     REALM_ASSERT_DEBUG(dynamic_cast<realm::ThreadSafeReference<Object> *>(reference.get()));
371     auto object_reference = static_cast<realm::ThreadSafeReference<Object> *>(reference.get());
372
373     Object object = realm->_realm->resolve_thread_safe_reference(std::move(*object_reference));
374     if (!object.is_valid()) {
375         return nil;
376     }
377     NSString *objectClassName = @(object.get_object_schema().name.c_str());
378
379     return RLMCreateObjectAccessor(realm, realm->_info[objectClassName], object.row().get_index());
380 }
381
382 @end
383
384 RLMRealm *RLMObjectBaseRealm(__unsafe_unretained RLMObjectBase *object) {
385     return object ? object->_realm : nil;
386 }
387
388 RLMObjectSchema *RLMObjectBaseObjectSchema(__unsafe_unretained RLMObjectBase *object) {
389     return object ? object->_objectSchema : nil;
390 }
391
392 id RLMObjectBaseObjectForKeyedSubscript(RLMObjectBase *object, NSString *key) {
393     if (!object) {
394         return nil;
395     }
396
397     if (object->_realm) {
398         return RLMDynamicGetByName(object, key, false);
399     }
400     else {
401         return [object valueForKey:key];
402     }
403 }
404
405 void RLMObjectBaseSetObjectForKeyedSubscript(RLMObjectBase *object, NSString *key, id obj) {
406     if (!object) {
407         return;
408     }
409
410     if (object->_realm) {
411         RLMDynamicValidatedSet(object, key, obj);
412     }
413     else {
414         [object setValue:obj forKey:key];
415     }
416 }
417
418
419 BOOL RLMObjectBaseAreEqual(RLMObjectBase *o1, RLMObjectBase *o2) {
420     // if not the correct types throw
421     if ((o1 && ![o1 isKindOfClass:RLMObjectBase.class]) || (o2 && ![o2 isKindOfClass:RLMObjectBase.class])) {
422         @throw RLMException(@"Can only compare objects of class RLMObjectBase");
423     }
424     // if identical object (or both are nil)
425     if (o1 == o2) {
426         return YES;
427     }
428     // if one is nil
429     if (o1 == nil || o2 == nil) {
430         return NO;
431     }
432     // if not in realm or differing realms
433     if (o1->_realm == nil || o1->_realm != o2->_realm) {
434         return NO;
435     }
436     // if either are detached
437     if (!o1->_row.is_attached() || !o2->_row.is_attached()) {
438         return NO;
439     }
440     // if table and index are the same
441     return o1->_row.get_table() == o2->_row.get_table()
442         && o1->_row.get_index() == o2->_row.get_index();
443 }
444
445 id RLMValidatedValueForProperty(id object, NSString *key, NSString *className) {
446     @try {
447         return [object valueForKey:key];
448     }
449     @catch (NSException *e) {
450         if ([e.name isEqualToString:NSUndefinedKeyException]) {
451             @throw RLMException(@"Invalid value '%@' to initialize object of type '%@': missing key '%@'",
452                                 object, className, key);
453         }
454         @throw;
455     }
456 }
457
458 Class RLMObjectUtilClass(BOOL isSwift) {
459     static Class objectUtilObjc = [RLMObjectUtil class];
460     static Class objectUtilSwift = NSClassFromString(@"RealmSwiftObjectUtil");
461     return isSwift && objectUtilSwift ? objectUtilSwift : objectUtilObjc;
462 }
463
464 @implementation RLMObjectUtil
465
466 + (NSArray *)ignoredPropertiesForClass:(Class)cls {
467     return [cls ignoredProperties];
468 }
469
470 + (NSArray *)indexedPropertiesForClass:(Class)cls {
471     return [cls indexedProperties];
472 }
473
474 + (NSDictionary *)linkingObjectsPropertiesForClass:(Class)cls {
475     return [cls linkingObjectsProperties];
476 }
477
478 + (NSDictionary *)linkingObjectProperties:(__unused id)object {
479     return nil;
480 }
481
482 + (NSArray *)getSwiftProperties:(__unused id)obj {
483     return nil;
484 }
485
486 + (NSDictionary *)getOptionalProperties:(__unused id)obj {
487     return nil;
488 }
489
490 + (NSArray *)requiredPropertiesForClass:(Class)cls {
491     return [cls requiredProperties];
492 }
493
494 @end
495
496 @implementation RLMSwiftPropertyMetadata
497
498 + (instancetype)metadataForOtherProperty:(NSString *)propertyName {
499     RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
500     md.propertyName = propertyName;
501     md.kind = RLMSwiftPropertyKindOther;
502     return md;
503 }
504
505 + (instancetype)metadataForListProperty:(NSString *)propertyName {
506     RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
507     md.propertyName = propertyName;
508     md.kind = RLMSwiftPropertyKindList;
509     return md;
510 }
511
512 + (instancetype)metadataForLinkingObjectsProperty:(NSString *)propertyName
513                                         className:(NSString *)className
514                                linkedPropertyName:(NSString *)linkedPropertyName {
515     RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
516     md.propertyName = propertyName;
517     md.className = className;
518     md.linkedPropertyName = linkedPropertyName;
519     md.kind = RLMSwiftPropertyKindLinkingObjects;
520     return md;
521 }
522
523 + (instancetype)metadataForOptionalProperty:(NSString *)propertyName type:(RLMPropertyType)type {
524     RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
525     md.propertyName = propertyName;
526     md.propertyType = type;
527     md.kind = RLMSwiftPropertyKindOptional;
528     return md;
529 }
530
531 + (instancetype)metadataForNilLiteralOptionalProperty:(NSString *)propertyName {
532     RLMSwiftPropertyMetadata *md = [RLMSwiftPropertyMetadata new];
533     md.propertyName = propertyName;
534     md.kind = RLMSwiftPropertyKindNilLiteralOptional;
535     return md;
536 }
537
538 @end