Move more mapping internal management for load/save into NJMapping. Clear out complic...
authorJoe Wreschnig <joe.wreschnig@gmail.com>
Sun, 10 Mar 2013 23:22:49 +0000 (00:22 +0100)
committerJoe Wreschnig <joe.wreschnig@gmail.com>
Sun, 10 Mar 2013 23:22:49 +0000 (00:22 +0100)
Classes/NJMapping.h
Classes/NJMapping.m
Classes/NJMappingsController.m
Info.plist

index 5653745a0562fd7532c5f82949ddb3e40978f1b2..8b800123f49a22fb4344a01cc67aa744c12e5de8 100644 (file)
 @interface NJMapping : NSObject
 
 @property (nonatomic, copy) NSString *name;
-@property (nonatomic, readonly) NSMutableDictionary *entries;
+@property (nonatomic, readonly) NSUInteger count;
+
++ (id)mappingWithContentsOfURL:(NSURL *)url
+                      mappings:(NSArray *)mappings
+                         error:(NSError **)error;
 
 - (id)initWithName:(NSString *)name;
+- (id)initWithSerialization:(NSDictionary *)serialization
+                   mappings:(NSArray *)mappings;
+
 - (NJOutput *)objectForKeyedSubscript:(NJInput *)input;
 - (void)setObject:(NJOutput *)output forKeyedSubscript:(NJInput *)input;
 - (NSDictionary *)serialize;
 - (BOOL)writeToURL:(NSURL *)url error:(NSError **)error;
-
-+ (id)mappingWithContentsOfURL:(NSURL *)url mappings:(NSArray *)mappings error:(NSError **)error;
+- (BOOL)hasConflictWith:(NJMapping *)other;
+- (void)mergeEntriesFrom:(NJMapping *)other;
 
 @end
index 8cc572407945ac02b21a1171123e94e884a16995..355dbccdf58951959b52f630ad841665626ccea7 100644 (file)
 #import "NJInput.h"
 #import "NJOutput.h"
 
-@implementation NJMapping
+@implementation NJMapping {
+    NSMutableDictionary *_entries;
+}
+
+// Extra checks during initialization because the data is often loaded
+// from untrusted serializations.
 
 - (id)initWithName:(NSString *)name {
     if ((self = [super init])) {
-        self.name = name ? name : @"Untitled";
+        self.name = [name isKindOfClass:NSString.class] ? name : @"Untitled";
         _entries = [[NSMutableDictionary alloc] init];
     }
     return self;
 }
 
+- (id)initWithSerialization:(NSDictionary *)serialization
+                   mappings:(NSArray *)mappings {
+    if ((self = [self initWithName:serialization[@"name"]])) {
+        NSDictionary *entries = serialization[@"entries"];
+        if ([entries isKindOfClass:NSDictionary.class]) {
+            for (id key in entries) {
+                if ([key isKindOfClass:NSString.class]) {
+                    NJOutput *output = [NJOutput outputDeserialize:entries[key]
+                                                      withMappings:mappings];
+                    if (output)
+                        _entries[key] = output;
+                }
+            }
+        }
+    }
+    return self;
+}
+
 - (NJOutput *)objectForKeyedSubscript:(NJInput *)input {
     return input ? _entries[input.uid] : nil;
 }
     return success;
 }
 
+- (NSUInteger)count {
+    return _entries.count;
+}
+
+- (BOOL)hasConflictWith:(NJMapping *)other {
+    if (other.count < self.count)
+        return [other hasConflictWith:self];
+    for (NSString *uid in _entries) {
+        NJOutput *output = other->_entries[uid];
+        if (output && ![output isEqual:_entries[uid]])
+            return YES;
+    }
+    return NO;
+}
+
 + (id)mappingWithContentsOfURL:(NSURL *)url mappings:(NSArray *)mappings error:(NSError **)error {
     NSInputStream *stream = [NSInputStream inputStreamWithURL:url];
     [stream open];
         return nil;
     }
     
-    NSDictionary *entries = serialization[@"entries"];
-    NJMapping *mapping = [[NJMapping alloc] initWithName:serialization[@"name"]];
-    for (id key in entries) {
-        NSDictionary *value = entries[key];
-        if ([key isKindOfClass:NSString.class]) {
-            NJOutput *output = [NJOutput outputDeserialize:value
-                                              withMappings:mappings];
-            if (output)
-                mapping.entries[key] = output;
-        }
-    }
-    return mapping;
+    return [[NJMapping alloc] initWithSerialization:serialization
+                                           mappings:mappings];
+}
+
+- (void)mergeEntriesFrom:(NJMapping *)other {
+    if (other)
+        [_entries addEntriesFromDictionary:other->_entries];
 }
 
 @end
index de66fb888f5199bdd348b40289ffa6412da2558a..5d861868ea572afe7493ce06ff773546369654af 100644 (file)
 - (void)loadAllFrom:(NSArray *)storedMappings andActivate:(NSUInteger)selected {
     NSMutableArray* newMappings = [[NSMutableArray alloc] initWithCapacity:storedMappings.count];
 
-    // have to do two passes in case mapping1 refers to mapping2 via a NJOutputMapping
+    // Requires two passes to deal with inter-mapping references. First make
+    // an empty mapping for each serialized mapping. Then, deserialize the
+    // data pointing to the empty mappings. Then merge that data back into
+    // its equivalent empty one, which is the one we finally use.
     for (NSDictionary *storedMapping in storedMappings) {
         NJMapping *mapping = [[NJMapping alloc] initWithName:storedMapping[@"name"]];
         [newMappings addObject:mapping];
     }
 
     for (unsigned i = 0; i < storedMappings.count; ++i) {
-        NSDictionary *entries = storedMappings[i][@"entries"];
-        NJMapping *mapping = newMappings[i];
-        for (id key in entries) {
-            NJOutput *output = [NJOutput outputDeserialize:entries[key]
-                                              withMappings:newMappings];
-            if (output)
-                mapping.entries[key] = output;
-        }
+        NJMapping *realMapping = [[NJMapping alloc] initWithSerialization:storedMappings[i]
+                                                                 mappings:newMappings];
+        [newMappings[i] mergeEntriesFrom:realMapping];
     }
     
     if (newMappings.count) {
                                                        error:&error];
     
     if (mapping && !error) {
-        BOOL conflict = NO;
         NJMapping *mergeInto = self[mapping.name];
-        for (id key in mapping.entries) {
-            if (mergeInto.entries[key]
-                && ![mergeInto.entries[key] isEqual:mapping.entries[key]]) {
-                conflict = YES;
-                break;
-            }
-        }
+        BOOL conflict = [mergeInto hasConflictWith:mapping];
         
         if (conflict) {
             NSAlert *conflictAlert = [[NSAlert alloc] init];
         }
         
         if (mergeInto) {
-            [mergeInto.entries addEntriesFromDictionary:mapping.entries];
+            [mergeInto mergeEntriesFrom:mapping];
             mapping = mergeInto;
         } else {
             [_mappings addObject:mapping];
index be682f8c3f4afd8b9fb0a4fec935ae8d84243c68..9d3612e9c4c326f2ca9b2f874490aaae244ea806 100644 (file)
@@ -46,7 +46,7 @@
        <key>CFBundleSignature</key>
        <string>????</string>
        <key>CFBundleVersion</key>
-       <string>106</string>
+       <string>108</string>
        <key>LSApplicationCategoryType</key>
        <string>public.app-category.utilities</string>
        <key>NSHumanReadableCopyright</key>