added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / RLMMigration.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 "RLMMigration_Private.h"
20
21 #import "RLMAccessor.h"
22 #import "RLMObject_Private.h"
23 #import "RLMObject_Private.hpp"
24 #import "RLMObjectSchema_Private.hpp"
25 #import "RLMObjectStore.h"
26 #import "RLMProperty_Private.h"
27 #import "RLMRealm_Dynamic.h"
28 #import "RLMRealm_Private.hpp"
29 #import "RLMResults_Private.hpp"
30 #import "RLMSchema_Private.hpp"
31 #import "RLMUtil.hpp"
32
33 #import "object_store.hpp"
34 #import "shared_realm.hpp"
35 #import "schema.hpp"
36
37 #import <realm/table.hpp>
38
39 using namespace realm;
40
41 // The source realm for a migration has to use a SharedGroup to be able to share
42 // the file with the destination realm, but we don't want to let the user call
43 // beginWriteTransaction on it as that would make no sense.
44 @interface RLMMigrationRealm : RLMRealm
45 @end
46
47 @implementation RLMMigrationRealm
48 - (BOOL)readonly {
49     return YES;
50 }
51
52 - (void)beginWriteTransaction {
53     @throw RLMException(@"Cannot modify the source Realm in a migration");
54 }
55 @end
56
57 @implementation RLMMigration {
58     realm::Schema *_schema;
59     NSMutableDictionary *deletedObjectIndices;
60     NSMutableSet *deletedClasses;
61 }
62
63 - (instancetype)initWithRealm:(RLMRealm *)realm oldRealm:(RLMRealm *)oldRealm schema:(realm::Schema &)schema {
64     self = [super init];
65     if (self) {
66         _realm = realm;
67         _oldRealm = oldRealm;
68         _schema = &schema;
69         object_setClass(_oldRealm, RLMMigrationRealm.class);
70         deletedObjectIndices = [NSMutableDictionary dictionary];
71         deletedClasses = [NSMutableSet set];
72     }
73     return self;
74 }
75
76 - (RLMSchema *)oldSchema {
77     return self.oldRealm.schema;
78 }
79
80 - (RLMSchema *)newSchema {
81     return self.realm.schema;
82 }
83
84 - (void)enumerateObjects:(NSString *)className block:(RLMObjectMigrationBlock)block {
85     if ([deletedClasses containsObject:className]) {
86         return;
87     }
88     
89     // get all objects
90     RLMResults *objects = [_realm.schema schemaForClassName:className] ? [_realm allObjects:className] : nil;
91     RLMResults *oldObjects = [_oldRealm.schema schemaForClassName:className] ? [_oldRealm allObjects:className] : nil;
92
93     if (objects && oldObjects) {
94         NSArray *deletedObjects = deletedObjectIndices[className];
95         if (!deletedObjects) {
96             deletedObjects = [NSMutableArray array];
97             deletedObjectIndices[className] = deletedObjects;
98         }
99
100         for (long i = oldObjects.count - 1; i >= 0; i--) {
101             @autoreleasepool {
102                 if ([deletedObjects containsObject:@(i)]) {
103                     continue;
104                 }
105                 block(oldObjects[i], objects[i]);
106             }
107         }
108     }
109     else if (objects) {
110         for (long i = objects.count - 1; i >= 0; i--) {
111             @autoreleasepool {
112                 block(nil, objects[i]);
113             }
114         }
115     }
116     else if (oldObjects) {
117         for (long i = oldObjects.count - 1; i >= 0; i--) {
118             @autoreleasepool {
119                 block(oldObjects[i], nil);
120             }
121         }
122     }
123 }
124
125 - (void)execute:(RLMMigrationBlock)block {
126     @autoreleasepool {
127         // disable all primary keys for migration and use DynamicObject for all types
128         for (RLMObjectSchema *objectSchema in _realm.schema.objectSchema) {
129             objectSchema.accessorClass = RLMDynamicObject.class;
130             objectSchema.primaryKeyProperty.isPrimary = NO;
131         }
132         for (RLMObjectSchema *objectSchema in _oldRealm.schema.objectSchema) {
133             objectSchema.accessorClass = RLMDynamicObject.class;
134         }
135
136         block(self, _oldRealm->_realm->schema_version());
137
138         [self deleteObjectsMarkedForDeletion];
139         [self deleteDataMarkedForDeletion];
140
141         _oldRealm = nil;
142         _realm = nil;
143     }
144 }
145
146 - (RLMObject *)createObject:(NSString *)className withValue:(id)value {
147     return [_realm createObject:className withValue:value];
148 }
149
150 - (RLMObject *)createObject:(NSString *)className withObject:(id)object {
151     return [self createObject:className withValue:object];
152 }
153
154 - (void)deleteObject:(RLMObject *)object {
155     [deletedObjectIndices[object.objectSchema.className] addObject:@(object->_row.get_index())];
156 }
157
158 - (void)deleteObjectsMarkedForDeletion {
159     for (NSString *className in deletedObjectIndices.allKeys) {
160         RLMResults *objects = [_realm allObjects:className];
161         for (NSNumber *index in deletedObjectIndices[className]) {
162             RLMObject *object = objects[index.longValue];
163             [_realm deleteObject:object];
164         }
165     }
166 }
167
168 - (BOOL)deleteDataForClassName:(NSString *)name {
169     if (!name) {
170         return false;
171     }
172
173     TableRef table = ObjectStore::table_for_object_type(_realm.group, name.UTF8String);
174     if (!table) {
175         return false;
176     }
177
178     [deletedClasses addObject:name];
179     return true;
180 }
181
182 - (void)deleteDataMarkedForDeletion {
183     for (NSString *className in deletedClasses) {
184         TableRef table = ObjectStore::table_for_object_type(_realm.group, className.UTF8String);
185         if (!table) {
186             continue;
187         }
188         if ([_realm.schema schemaForClassName:className]) {
189             table->clear();
190         }
191         else {
192             realm::ObjectStore::delete_data_for_object(_realm.group, className.UTF8String);
193         }
194     }
195 }
196
197 - (void)renamePropertyForClass:(NSString *)className oldName:(NSString *)oldName newName:(NSString *)newName {
198     const char *objectType = className.UTF8String;
199     realm::ObjectStore::rename_property(_realm.group, *_schema, objectType, oldName.UTF8String, newName.UTF8String);
200 }
201
202 @end