From: Joe Wreschnig Date: Sun, 10 Mar 2013 23:22:49 +0000 (+0100) Subject: Move more mapping internal management for load/save into NJMapping. Clear out complic... X-Git-Tag: version-1.1~63 X-Git-Url: https://git.yukkurigames.com/?p=enjoyable.git;a=commitdiff_plain;h=93c9d4bc39c6addbab6dbc7878d3684e42bbdc01 Move more mapping internal management for load/save into NJMapping. Clear out complications in NJMappingController. --- diff --git a/Classes/NJMapping.h b/Classes/NJMapping.h index 5653745..8b80012 100644 --- a/Classes/NJMapping.h +++ b/Classes/NJMapping.h @@ -12,14 +12,21 @@ @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 diff --git a/Classes/NJMapping.m b/Classes/NJMapping.m index 8cc5724..355dbcc 100644 --- a/Classes/NJMapping.m +++ b/Classes/NJMapping.m @@ -10,16 +10,39 @@ #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; } @@ -54,6 +77,21 @@ 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]; @@ -74,18 +112,13 @@ 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 diff --git a/Classes/NJMappingsController.m b/Classes/NJMappingsController.m index de66fb8..5d86186 100644 --- a/Classes/NJMappingsController.m +++ b/Classes/NJMappingsController.m @@ -167,21 +167,19 @@ - (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) { @@ -201,15 +199,8 @@ 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]; @@ -230,7 +221,7 @@ } if (mergeInto) { - [mergeInto.entries addEntriesFromDictionary:mapping.entries]; + [mergeInto mergeEntriesFrom:mapping]; mapping = mergeInto; } else { [_mappings addObject:mapping]; diff --git a/Info.plist b/Info.plist index be682f8..9d3612e 100644 --- a/Info.plist +++ b/Info.plist @@ -46,7 +46,7 @@ CFBundleSignature ???? CFBundleVersion - 106 + 108 LSApplicationCategoryType public.app-category.utilities NSHumanReadableCopyright