added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / RLMSchema.mm
diff --git a/iOS/Pods/Realm/Realm/RLMSchema.mm b/iOS/Pods/Realm/Realm/RLMSchema.mm
new file mode 100644 (file)
index 0000000..4d4c5eb
--- /dev/null
@@ -0,0 +1,361 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2014 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#import "RLMSchema_Private.h"
+
+#import "RLMAccessor.h"
+#import "RLMObjectBase_Private.h"
+#import "RLMObject_Private.hpp"
+#import "RLMObjectSchema_Private.hpp"
+#import "RLMProperty_Private.h"
+#import "RLMRealm_Private.hpp"
+#import "RLMSwiftSupport.h"
+#import "RLMUtil.hpp"
+
+#import "object_store.hpp"
+#import "schema.hpp"
+
+#import <realm/group.hpp>
+
+#import <objc/runtime.h>
+#include <mutex>
+
+using namespace realm;
+
+const uint64_t RLMNotVersioned = realm::ObjectStore::NotVersioned;
+
+// RLMSchema private properties
+@interface RLMSchema ()
+@property (nonatomic, readwrite) NSMutableDictionary *objectSchemaByName;
+@end
+
+// Private RLMSchema subclass that skips class registration on lookup
+@interface RLMPrivateSchema : RLMSchema
+@end
+@implementation RLMPrivateSchema
+- (RLMObjectSchema *)schemaForClassName:(NSString *)className {
+    return self.objectSchemaByName[className];
+}
+
+- (RLMObjectSchema *)objectForKeyedSubscript:(__unsafe_unretained NSString *const)className {
+    return [self schemaForClassName:className];
+}
+@end
+
+static RLMSchema *s_sharedSchema = [[RLMSchema alloc] init];
+static NSMutableDictionary *s_localNameToClass = [[NSMutableDictionary alloc] init];
+static RLMSchema *s_privateSharedSchema = [[RLMPrivateSchema alloc] init];
+
+static enum class SharedSchemaState {
+    Uninitialized,
+    Initializing,
+    Initialized
+} s_sharedSchemaState = SharedSchemaState::Uninitialized;
+
+@implementation RLMSchema {
+    NSArray *_objectSchema;
+    realm::Schema _objectStoreSchema;
+}
+
+// Caller must @synchronize on s_localNameToClass
+static RLMObjectSchema *RLMRegisterClass(Class cls) {
+    if (RLMObjectSchema *schema = s_privateSharedSchema[[cls className]]) {
+        return schema;
+    }
+
+    auto prevState = s_sharedSchemaState;
+    s_sharedSchemaState = SharedSchemaState::Initializing;
+    RLMObjectSchema *schema = [RLMObjectSchema schemaForObjectClass:cls];
+    s_sharedSchemaState = prevState;
+
+    // set unmanaged class on shared shema for unmanaged object creation
+    schema.unmanagedClass = RLMUnmanagedAccessorClassForObjectClass(schema.objectClass, schema);
+
+    // override sharedSchema class methods for performance
+    RLMReplaceSharedSchemaMethod(cls, schema);
+
+    s_privateSharedSchema.objectSchemaByName[schema.className] = schema;
+    if ([cls shouldIncludeInDefaultSchema] && prevState != SharedSchemaState::Initialized) {
+        s_sharedSchema.objectSchemaByName[schema.className] = schema;
+    }
+
+    return schema;
+}
+
+// Caller must @synchronize on s_localNameToClass
+static void RLMRegisterClassLocalNames(Class *classes, NSUInteger count) {
+    for (NSUInteger i = 0; i < count; i++) {
+        Class cls = classes[i];
+        if (!RLMIsObjectSubclass(cls)) {
+            continue;
+        }
+
+        NSString *className = NSStringFromClass(cls);
+        if ([className hasPrefix:@"RLM:"] || [className hasPrefix:@"NSKVONotifying"]) {
+            continue;
+        }
+
+        if ([RLMSwiftSupport isSwiftClassName:className]) {
+            className = [RLMSwiftSupport demangleClassName:className];
+        }
+        // NSStringFromClass demangles the names for top-level Swift classes
+        // but not for nested classes. _T indicates it's a Swift symbol, t
+        // indicates it's a type, and C indicates it's a class.
+        else if ([className hasPrefix:@"_TtC"]) {
+            @throw RLMException(@"RLMObject subclasses cannot be nested within other declarations. Please move %@ to global scope.", className);
+        }
+
+        if (Class existingClass = s_localNameToClass[className]) {
+            if (existingClass != cls) {
+                @throw RLMException(@"RLMObject subclasses with the same name cannot be included twice in the same target. "
+                                    @"Please make sure '%@' is only linked once to your current target.", className);
+            }
+            continue;
+        }
+
+        s_localNameToClass[className] = cls;
+        RLMReplaceClassNameMethod(cls, className);
+    }
+}
+
+- (instancetype)init {
+    self = [super init];
+    if (self) {
+        _objectSchemaByName = [[NSMutableDictionary alloc] init];
+    }
+    return self;
+}
+
+- (NSArray *)objectSchema {
+    if (!_objectSchema) {
+        _objectSchema = [_objectSchemaByName allValues];
+    }
+    return _objectSchema;
+}
+
+- (void)setObjectSchema:(NSArray *)objectSchema {
+    _objectSchema = objectSchema;
+    _objectSchemaByName = [NSMutableDictionary dictionaryWithCapacity:objectSchema.count];
+    for (RLMObjectSchema *object in objectSchema) {
+        [_objectSchemaByName setObject:object forKey:object.className];
+    }
+}
+
+- (RLMObjectSchema *)schemaForClassName:(NSString *)className {
+    if (RLMObjectSchema *schema = _objectSchemaByName[className]) {
+        return schema; // fast path for already-initialized schemas
+    } else if (Class cls = [RLMSchema classForString:className]) {
+        [cls sharedSchema];                    // initialize the schema
+        return _objectSchemaByName[className]; // try again
+    } else {
+        return nil;
+    }
+}
+
+- (RLMObjectSchema *)objectForKeyedSubscript:(__unsafe_unretained NSString *const)className {
+    RLMObjectSchema *schema = [self schemaForClassName:className];
+    if (!schema) {
+        @throw RLMException(@"Object type '%@' not managed by the Realm", className);
+    }
+    return schema;
+}
+
++ (instancetype)schemaWithObjectClasses:(NSArray *)classes {
+    NSUInteger count = classes.count;
+    auto classArray = std::make_unique<__unsafe_unretained Class[]>(count);
+    [classes getObjects:classArray.get() range:NSMakeRange(0, count)];
+
+    RLMSchema *schema = [[self alloc] init];
+    @synchronized(s_localNameToClass) {
+        RLMRegisterClassLocalNames(classArray.get(), count);
+
+        schema->_objectSchemaByName = [NSMutableDictionary dictionaryWithCapacity:count];
+        for (Class cls in classes) {
+            if (!RLMIsObjectSubclass(cls)) {
+                @throw RLMException(@"Can't add non-Object type '%@' to a schema.", cls);
+            }
+            schema->_objectSchemaByName[[cls className]] = RLMRegisterClass(cls);
+        }
+    }
+
+    NSMutableArray *errors = [NSMutableArray new];
+    // Verify that all of the targets of links are included in the class list
+    [schema->_objectSchemaByName enumerateKeysAndObjectsUsingBlock:^(id, RLMObjectSchema *objectSchema, BOOL *) {
+        for (RLMProperty *prop in objectSchema.properties) {
+            if (prop.type != RLMPropertyTypeObject) {
+                continue;
+            }
+            if (!schema->_objectSchemaByName[prop.objectClassName]) {
+                [errors addObject:[NSString stringWithFormat:@"- '%@.%@' links to class '%@', which is missing from the list of classes managed by the Realm", objectSchema.className, prop.name, prop.objectClassName]];
+            }
+        }
+    }];
+    if (errors.count) {
+        @throw RLMException(@"Invalid class subset list:\n%@", [errors componentsJoinedByString:@"\n"]);
+    }
+
+    return schema;
+}
+
++ (RLMObjectSchema *)sharedSchemaForClass:(Class)cls {
+    @synchronized(s_localNameToClass) {
+        // We create instances of Swift objects during schema init, and they
+        // obviously need to not also try to initialize the schema
+        if (s_sharedSchemaState == SharedSchemaState::Initializing) {
+            return nil;
+        }
+
+        RLMRegisterClassLocalNames(&cls, 1);
+        RLMObjectSchema *objectSchema = RLMRegisterClass(cls);
+        [cls initializeLinkedObjectSchemas];
+        return objectSchema;
+    }
+}
+
++ (instancetype)partialSharedSchema {
+    return s_sharedSchema;
+}
+
++ (instancetype)partialPrivateSharedSchema {
+    return s_privateSharedSchema;
+}
+
+// schema based on runtime objects
++ (instancetype)sharedSchema {
+    @synchronized(s_localNameToClass) {
+        // We replace this method with one which just returns s_sharedSchema
+        // once initialization is complete, but we still need to check if it's
+        // already complete because it may have been done by another thread
+        // while we were waiting for the lock
+        if (s_sharedSchemaState == SharedSchemaState::Initialized) {
+            return s_sharedSchema;
+        }
+
+        if (s_sharedSchemaState == SharedSchemaState::Initializing) {
+            @throw RLMException(@"Illegal recursive call of +[%@ %@]. Note: Properties of Swift `Object` classes must not be prepopulated with queried results from a Realm.", self, NSStringFromSelector(_cmd));
+        }
+
+        s_sharedSchemaState = SharedSchemaState::Initializing;
+        try {
+            // Make sure we've discovered all classes
+            {
+                unsigned int numClasses;
+                using malloc_ptr = std::unique_ptr<__unsafe_unretained Class[], decltype(&free)>;
+                malloc_ptr classes(objc_copyClassList(&numClasses), &free);
+                RLMRegisterClassLocalNames(classes.get(), numClasses);
+            }
+
+            [s_localNameToClass enumerateKeysAndObjectsUsingBlock:^(NSString *, Class cls, BOOL *) {
+                RLMRegisterClass(cls);
+            }];
+        }
+        catch (...) {
+            s_sharedSchemaState = SharedSchemaState::Uninitialized;
+            throw;
+        }
+
+        // Replace this method with one that doesn't need to acquire a lock
+        Class metaClass = objc_getMetaClass(class_getName(self));
+        IMP imp = imp_implementationWithBlock(^{ return s_sharedSchema; });
+        class_replaceMethod(metaClass, @selector(sharedSchema), imp, "@@:");
+
+        s_sharedSchemaState = SharedSchemaState::Initialized;
+    }
+
+    return s_sharedSchema;
+}
+
+// schema based on tables in a realm
++ (instancetype)dynamicSchemaFromObjectStoreSchema:(Schema const&)objectStoreSchema {
+    // cache descriptors for all subclasses of RLMObject
+    NSMutableArray *schemaArray = [NSMutableArray arrayWithCapacity:objectStoreSchema.size()];
+    for (auto &objectSchema : objectStoreSchema) {
+        RLMObjectSchema *schema = [RLMObjectSchema objectSchemaForObjectStoreSchema:objectSchema];
+        [schemaArray addObject:schema];
+    }
+
+    // set class array and mapping
+    RLMSchema *schema = [RLMSchema new];
+    schema.objectSchema = schemaArray;
+    return schema;
+}
+
++ (Class)classForString:(NSString *)className {
+    if (Class cls = s_localNameToClass[className]) {
+        return cls;
+    }
+
+    if (Class cls = NSClassFromString(className)) {
+        return RLMIsObjectSubclass(cls) ? cls : nil;
+    }
+
+    // className might be the local name of a Swift class we haven't registered
+    // yet, so scan them all then recheck
+    {
+        unsigned int numClasses;
+        std::unique_ptr<__unsafe_unretained Class[], decltype(&free)> classes(objc_copyClassList(&numClasses), &free);
+        RLMRegisterClassLocalNames(classes.get(), numClasses);
+    }
+
+    return s_localNameToClass[className];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    RLMSchema *schema = [[RLMSchema allocWithZone:zone] init];
+    schema->_objectSchemaByName = [[NSMutableDictionary allocWithZone:zone]
+                                   initWithDictionary:_objectSchemaByName copyItems:YES];
+    return schema;
+}
+
+- (BOOL)isEqualToSchema:(RLMSchema *)schema {
+    if (_objectSchemaByName.count != schema->_objectSchemaByName.count) {
+        return NO;
+    }
+    __block BOOL matches = YES;
+    [_objectSchemaByName enumerateKeysAndObjectsUsingBlock:^(NSString *name, RLMObjectSchema *objectSchema, BOOL *stop) {
+        if (![schema->_objectSchemaByName[name] isEqualToObjectSchema:objectSchema]) {
+            *stop = YES;
+            matches = NO;
+        }
+    }];
+    return matches;
+}
+
+- (NSString *)description {
+    NSMutableString *objectSchemaString = [NSMutableString string];
+    NSArray *sort = @[[NSSortDescriptor sortDescriptorWithKey:@"className" ascending:YES]];
+    for (RLMObjectSchema *objectSchema in [self.objectSchema sortedArrayUsingDescriptors:sort]) {
+        [objectSchemaString appendFormat:@"\t%@\n",
+         [objectSchema.description stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]];
+    }
+    return [NSString stringWithFormat:@"Schema {\n%@}", objectSchemaString];
+}
+
+- (Schema)objectStoreCopy {
+    if (_objectStoreSchema.size() == 0) {
+        std::vector<realm::ObjectSchema> schema;
+        schema.reserve(_objectSchemaByName.count);
+        [_objectSchemaByName enumerateKeysAndObjectsUsingBlock:[&](NSString *, RLMObjectSchema *objectSchema, BOOL *) {
+            schema.push_back(objectSchema.objectStoreCopy);
+        }];
+        _objectStoreSchema = std::move(schema);
+    }
+    return _objectStoreSchema;
+}
+
+@end