// // HMJSONCommand.m // KCD // // Created by Hori,Masaki on 2014/02/09. // Copyright (c) 2014年 Hori,Masaki. All rights reserved. // #import "HMJSONCommand.h" #import "HMIgnoreCommand.h" #import "HMUnknownComand.h" #import "HMFailedCommand.h" #import "HMAppDelegate.h" #import "HMServerDataStore.h" #if ENABLE_JSON_LOG #import "HMJSONNode.h" #import "HMCompositCommand.h" #import "HMJSONViewCommand.h" #endif static NSMutableArray *registeredCommands = nil; @interface HMJSONCommand () @property (strong, readwrite) NSDictionary *arguments; @property (copy, readwrite) NSString *api; @property (strong, readwrite) id json; #if ENABLE_JSON_LOG @property (strong, readwrite) NSArray *jsonTree; @property (strong, readwrite) NSArray *argumentArray; #endif @end @implementation HMJSONCommand @synthesize argumentsString = _argumentsString; @synthesize jsonData = _jsonData; + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ registeredCommands = [NSMutableArray new]; }); } + (HMJSONCommand *)commandForAPIResult:(HMAPIResult *)apiResult { HMJSONCommand *command = nil; if(!apiResult.json) { command = [HMFailedCommand new]; command.api = apiResult.api; command.arguments = apiResult.parameter; command.json = apiResult.json; return command; } #if ENABLE_JSON_LOG HMJSONViewCommand *viewCommand = [HMJSONViewCommand new]; viewCommand.api = apiResult.api; viewCommand.arguments = apiResult.parameter; viewCommand.json = apiResult.json; viewCommand.argumentArray = apiResult.argumentArray; id json = [HMJSONNode nodeWithJSON:apiResult.json]; if(json) { viewCommand.jsonTree = @[json]; } viewCommand.recieveDate = apiResult.date; command = viewCommand; #endif for(Class commandClass in registeredCommands) { if([commandClass canExcuteAPI:apiResult.api]) { command = [commandClass new]; command.api = apiResult.api; command.arguments = apiResult.parameter; command.json = apiResult.json; #if ENABLE_JSON_LOG_HANDLED_API command = [HMCompositCommand compositCommandWithCommands:command, viewCommand, nil]; #endif return command; } } #if ENABLE_JSON_LOG if(command == viewCommand) command = nil; #endif if(!command) { if([HMIgnoreCommand canExcuteAPI:apiResult.api]) { command = [HMIgnoreCommand new]; command.api = apiResult.api; command.arguments = apiResult.parameter; command.json = apiResult.json; #if ENABLE_JSON_LOG_HANDLED_API command = [HMCompositCommand compositCommandWithCommands:command, viewCommand, nil]; #endif } } if(!command) { command = [HMUnknownComand new]; command.api = apiResult.api; command.arguments = apiResult.parameter; command.json = apiResult.json; #if ENABLE_JSON_LOG_HANDLED_API command = [HMCompositCommand compositCommandWithCommands:command, viewCommand, nil]; #endif } return command; } + (void)registerClass:(Class)commandClass { if(!commandClass) return; if([registeredCommands containsObject:commandClass]) return; [registeredCommands addObject:commandClass]; } NSString *keyByDeletingPrefix(NSString *key) { NSString *newKye = nil; @try { newKye = [key substringFromIndex:4]; } @catch (NSException *exception) { NSLog(@"key is %@", key); } return newKye; } - (void)log:(NSString *)format, ... { va_list ap; va_start(ap, format); NSString *str = [[NSString alloc] initWithFormat:format arguments:ap]; NSLog(@"API: %@, Arguments: %@.\n%@", self.api, self.arguments, str); va_end(ap); } - (NSString *)dataKey { return @"api_data"; } - (NSString *)primaryKey { return @"id"; } - (void)setValueIfNeeded:(id)value toObject:(id)object forKey:(NSString *)key { id oldValue = [object valueForKey:key]; if(![oldValue isEqual:value]) { [object willChangeValueForKey:key]; [object setValue:value forKey:key]; [object didChangeValueForKey:key]; } } - (void)registerElement:(NSDictionary *)element toObject:(NSManagedObject *)object { if(!object) return; [self beginRegisterObject:object]; for(NSString *key in element) { if([self.ignoreKeys containsObject:key]) continue; id value = element[key]; if([self handleExtraValue:value forKey:key toObject:object]) { continue; } if([value isKindOfClass:[NSArray class]]) { NSUInteger i = 0; for(id element in value) { id hoge = element; NSString *newKey = [NSString stringWithFormat:@"%@_%ld", key, i]; if([object validateValue:&hoge forKey:newKey error:NULL]) { [self setValueIfNeeded:hoge toObject:object forKey:newKey]; } i++; } } else if([value isKindOfClass:[NSDictionary class]]) { for(id subKey in value) { id subValue = value[subKey]; NSString *newKey = [NSString stringWithFormat:@"%@_D_%@", key, keyByDeletingPrefix(subKey)]; if([object validateValue:&subValue forKey:newKey error:NULL]) { [self setValueIfNeeded:subValue toObject:object forKey:newKey]; } } } else { if([object validateValue:&value forKey:key error:NULL]) { [self setValueIfNeeded:value toObject:object forKey:key]; } } } } - (void)commitJSONToEntityNamed:(NSString *)entityName { NSArray *api_data = [self.json valueForKeyPath:self.dataKey]; if([api_data isKindOfClass:[NSDictionary class]]) { api_data = @[api_data]; } if(![api_data isKindOfClass:[NSArray class]]) { [self log:@"api_data is NOT NSArray."]; return; } NSString *primaryKey = self.primaryKey; NSString *primaryAPIKey = [NSString stringWithFormat:@"api_%@", primaryKey]; HMServerDataStore *serverDataStore = [HMServerDataStore oneTimeEditor]; NSManagedObjectContext *managedObjectContext = [serverDataStore managedObjectContext]; NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:primaryKey ascending:YES]; NSError *error = nil; NSArray *objects = [serverDataStore objectsWithEntityName:entityName sortDescriptors:@[sortDescriptor] predicate:nil error:&error]; if(error) { [self log:@"Fetch error: %@", error]; return; } NSRange range = NSMakeRange(0, objects.count); for(NSDictionary *element in api_data) { NSUInteger index = [objects indexOfObject:element[primaryAPIKey] inSortedRange:range options:NSBinarySearchingFirstEqual usingComparator:^(id obj1, id obj2) { id value1, value2; if([obj1 isKindOfClass:[NSNumber class]]) { value1 = obj1; } else { value1 = [obj1 valueForKey:primaryKey]; } if([obj2 isKindOfClass:[NSNumber class]]) { value2 = obj2; } else { value2 = [obj2 valueForKey:primaryKey]; } return [value1 compare:value2]; }]; NSManagedObject *object = nil; if(index == NSNotFound) { object = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:managedObjectContext]; } else { object = objects[index]; } [self registerElement:element toObject:object]; } [self finishOperating:managedObjectContext]; } // abstruct - (void)execute { NSLog(@"Enter %s", __PRETTY_FUNCTION__); assert(NO); } + (BOOL)canExcuteAPI:(NSString *)api { return NO; } - (void)beginRegisterObject:(NSManagedObject *)object { } - (BOOL)handleExtraValue:(id)value forKey:(NSString *)key toObject:(NSManagedObject *)object { return NO; } - (void)finishOperating:(NSManagedObjectContext *)moc { } @end