// // IBTBaseEntity.m // // // Created by Xummer on 15/4/7. // Copyright (c) 2015年 Xummer. All rights reserved. // #import "IBTModel.h" #import "FMDB.h" #import "ICRDataBaseController.h" #import @implementation IBTModel @synthesize arrSubModels; - (instancetype)init { self = [super init]; if (!self) { return nil; } self.arrSubModels = [NSMutableArray array]; return self; } #pragma mark - Setting + (NSDictionary *)specialKeysAndReplaceKeys { return nil; } + (NSArray *)customAcitonKeys { return nil; } + (NSArray *)localKeys { return nil; } #pragma mark - ICRDatabaseObject + (id)DBObject { return [[[self class] alloc] init]; } + (id)objectFromFetchResult:(FMResultSet *)result { id obj = [[[self class] alloc] init]; [obj updateFromFetchResult:result]; return obj; } - (void)updateFromFetchResult:(FMResultSet *)result { NSDictionary *dictSpKeyDict = [[self class] specialKeysAndReplaceKeys]; NSArray *arrSpecialKeys = [dictSpKeyDict allKeys]; NSArray *arrCustomKeys = [[self class] customAcitonKeys]; unsigned int count; objc_property_t *properties = class_copyPropertyList([self class], &count); // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1 NSString *objectType = [NSString stringWithUTF8String:@encode(id)]; for (unsigned int i = 0; i < count; i++) { objc_property_t property = properties[i]; NSString *key = [NSString stringWithUTF8String:property_getName(property)]; NSString *keyInJson = key; if ([arrCustomKeys containsObject:key]) { continue; } else if ([arrSpecialKeys containsObject:key]) { keyInJson = dictSpKeyDict[ key ]; } NSString *typeAttribute = [[[NSString stringWithUTF8String:property_getAttributes(property)] componentsSeparatedByString:@","] firstObject]; NSString *nsPropertyType = [typeAttribute substringFromIndex:1]; const char *propertyType = [nsPropertyType UTF8String]; // CLog(@"Key %@, type %@", key, typeAttribute); if ([nsPropertyType hasPrefix:objectType]) { NSString * typeClassName = [typeAttribute substringWithRange:NSMakeRange(3, [typeAttribute length] - 4)]; //turns @"NSString" into NSString Class typeClass = NSClassFromString(typeClassName); if (typeClass == [NSString class]) { [self setValue:[result stringForColumn:keyInJson] forKey:key]; } else if (typeClass == [NSDate class]) { [self setValue:[result dateForColumn:keyInJson] forKey:key]; } else if (typeClass == [NSArray class] || typeClass == [NSDictionary class]) { NSData *data = [result dataForColumn:keyInJson]; [self setValue:[NSKeyedUnarchiver unarchiveObjectWithData:data] forKey:keyInJson]; } else { [self setValue:[result objectForColumnName:keyInJson] forKey:key]; } } else if (strcmp(propertyType, @encode(BOOL)) == 0) { [self setValue:@( [result boolForColumn:keyInJson] ) forKey:key]; } else if (strcmp(propertyType, @encode(unsigned long long)) == 0 || strcmp(propertyType, @encode(long long)) == 0) { [self setValue:@( [result longLongIntForColumn:keyInJson] ) forKey:key]; } else if (strcmp(propertyType, @encode(double)) == 0 || strcmp(propertyType, @encode(float)) == 0) { [self setValue:@( [result doubleForColumn:keyInJson] ) forKey:key]; } else if (strcmp(propertyType, @encode(long)) == 0 || strcmp(propertyType, @encode(unsigned long)) == 0) { [self setValue:@( [result longForColumn:keyInJson] ) forKey:key]; } else if (strcmp(propertyType, @encode(int)) == 0 || strcmp(propertyType, @encode(unsigned int)) == 0 || strcmp(propertyType, @encode(short)) == 0 || strcmp(propertyType, @encode(unsigned short)) == 0) { [self setValue:@( [result intForColumn:keyInJson] ) forKey:key]; } else if (strcmp(propertyType, @encode(char)) == 0 || strcmp(propertyType, @encode(unsigned char)) == 0) { [self setValue:[NSString stringWithUTF8String:(const char *)[result UTF8StringForColumnName:keyInJson]] forKey:key]; } /* TODO dataNoCopyForColumn: */ } free(properties); } #pragma mark - SQLite + (NSString *)TableName { return NSStringFromClass([self class]); } + (NSString *)PrimaryKey { return nil; } + (NSArray *)PrimaryKeys { NSString *priKey = [[self class] PrimaryKey]; if (priKey) { return @[ priKey ]; } else { return nil; } } - (NSString *)SQLForInsertOrUpdate:(NSArray * __autoreleasing *)pValues { unsigned int count; objc_property_t *properties = class_copyPropertyList([self class], &count); if (count == 0) { free(properties); return nil; } NSDictionary *dictSpKeyDict = [[self class] specialKeysAndReplaceKeys]; NSArray *arrSpecialKeys = [dictSpKeyDict allKeys]; NSArray *arrCustomKeys = [[self class] customAcitonKeys]; NSMutableArray *arrValues = [NSMutableArray array]; NSMutableArray *arrKeys = [NSMutableArray array]; for (unsigned int i = 0; i < count; i++) { objc_property_t property = properties[i]; NSString *key = [NSString stringWithUTF8String:property_getName(property)]; id obj = [self valueForKey:key]; if (!obj) { continue; } NSString *keyInJson = key; if ([arrCustomKeys containsObject:key]) { continue; } else if ([arrSpecialKeys containsObject:key]) { keyInJson = dictSpKeyDict[ key ]; } [arrKeys addObject:keyInJson]; if ([obj isKindOfClass:[NSArray class]] || [obj isKindOfClass:[NSDictionary class]]) { obj = [NSKeyedArchiver archivedDataWithRootObject:obj]; } [arrValues addObject:obj]; } free(properties); if (pValues) { *pValues = arrValues; } return [NSString stringWithFormat:@"REPLACE INTO '%@' (%@) VALUES %@", [[self class] TableName], [arrKeys componentsJoinedByString:@","], [[self class] ValuePlaceholdersWithCount:[arrValues count]]]; } + (NSString *)SQLForCreateTable { unsigned int count; objc_property_t *properties = class_copyPropertyList([self class], &count); if (count == 0) { free(properties); return nil; } NSDictionary *dictSpKeyDict = [[self class] specialKeysAndReplaceKeys]; NSArray *arrSpecialKeys = [dictSpKeyDict allKeys]; NSArray *arrCustomKeys = [[self class] customAcitonKeys]; NSArray *arrPrimaryKeys = [[self class] PrimaryKeys]; // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1 NSString *objectType = [NSString stringWithUTF8String:@encode(id)]; NSString *nsTypeStr = nil; NSMutableString *nsTmp = nil; NSMutableArray *arrColNameAndTypes = [NSMutableArray array]; for (unsigned int i = 0; i < count; i++) { nsTypeStr = @"TEXT"; nsTmp = [NSMutableString stringWithString:@""]; objc_property_t property = properties[ i ]; NSString *key = [NSString stringWithUTF8String:property_getName(property)]; NSString *keyInJson = key; if ([arrCustomKeys containsObject:key]) { continue; } else if ([arrSpecialKeys containsObject:key]) { keyInJson = dictSpKeyDict[ key ]; } NSString *typeAttribute = [[[NSString stringWithUTF8String:property_getAttributes(property)] componentsSeparatedByString:@","] firstObject]; NSString *nsPropertyType = [typeAttribute substringFromIndex:1]; const char *propertyType = [nsPropertyType UTF8String]; if ([nsPropertyType hasPrefix:objectType]) { NSString * typeClassName = [typeAttribute substringWithRange:NSMakeRange(3, [typeAttribute length] - 4)]; //turns @"NSString" into NSString Class typeClass = NSClassFromString(typeClassName); if (typeClass == [NSString class]) { nsTypeStr = @"TEXT"; } else if (typeClass == [NSDate class]) { nsTypeStr = @"TEXT"; } else if (typeClass == [NSArray class] || typeClass == [NSDictionary class]) { nsTypeStr = @"BLOB"; } else { nsTypeStr = @"TEXT"; } } else if (strcmp(propertyType, @encode(BOOL)) == 0) { nsTypeStr = @"INTEGER"; } else if (strcmp(propertyType, @encode(unsigned long long)) == 0 || strcmp(propertyType, @encode(long long)) == 0) { nsTypeStr = @"INTEGER"; } else if (strcmp(propertyType, @encode(double)) == 0 || strcmp(propertyType, @encode(float)) == 0) { nsTypeStr = @"REAL"; } else if (strcmp(propertyType, @encode(long)) == 0 || strcmp(propertyType, @encode(unsigned long)) == 0) { nsTypeStr = @"INTEGER"; } else if (strcmp(propertyType, @encode(int)) == 0 || strcmp(propertyType, @encode(unsigned int)) == 0 || strcmp(propertyType, @encode(short)) == 0 || strcmp(propertyType, @encode(unsigned short)) == 0) { nsTypeStr = @"INTEGER"; } else if (strcmp(propertyType, @encode(char)) == 0 || strcmp(propertyType, @encode(unsigned char)) == 0) { nsTypeStr = @"TEXT"; } else { nsTypeStr = @"TEXT"; } [nsTmp appendFormat:@"'%@' %@", keyInJson, nsTypeStr]; [arrColNameAndTypes addObject:nsTmp]; } if ([arrPrimaryKeys count] > 0) { NSString *nsPriKeys = [NSString stringWithFormat:@"PRIMARY KEY (%@)", [arrPrimaryKeys componentsJoinedByString:@","]]; [arrColNameAndTypes addObject:nsPriKeys]; } free(properties); return [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS '%@' (%@)", [[self class] TableName], [arrColNameAndTypes componentsJoinedByString:@","]]; } + (NSString *)SQLForUpdateKeyValueDict:(NSDictionary *)kvDict WhereInfoStr:(NSString *)nsWhereInfoStr { if ([kvDict isKindOfClass:[NSDictionary class]] && [kvDict count] > 0) { NSMutableArray *arrKeyValues = [NSMutableArray array]; for (NSString *key in [kvDict allKeys]) { NSString *nsTmp = [NSString stringWithFormat:@"%@=%@", key, kvDict[ key ]]; [arrKeyValues safeAddObject:nsTmp]; } return [NSString stringWithFormat:@"UPDATE '%@' SET %@ WHERE %@", [[self class] TableName], [arrKeyValues componentsJoinedByString:@","], nsWhereInfoStr ]; } else { return nil; } } + (NSString *)SQLForUpdateKeyValueDict:(NSDictionary *)kvDict WhereInfo:(NSDictionary *)dictWhere { if ([dictWhere isKindOfClass:[NSDictionary class]] && [dictWhere count]) { NSMutableArray *arrWhereInfo = [NSMutableArray array]; for (NSString *key in [dictWhere allKeys]) { NSString *nsTmp = [NSString stringWithFormat:@"%@=%@", key, dictWhere[ key ]]; [arrWhereInfo safeAddObject:nsTmp]; } return [[self class] SQLForUpdateKeyValueDict:kvDict WhereInfoStr:[arrWhereInfo componentsJoinedByString:@" AND "]]; } else { return nil; } } - (void)saveToDBWithHandleData:(void (^)(id ))handleDataBlock complete:(void (^)(void))complete fail:(void (^)(NSError *))fail { ICRDataBaseController *dbCtrl = [ICRDataBaseController sharedController]; [dbCtrl storageDBObject:self handleData:handleDataBlock complete:complete fail:fail]; } #pragma mark - Functions - (NSDictionary *)dictForCommit { unsigned int count; objc_property_t *properties = class_copyPropertyList([self class], &count); if (count == 0) { free(properties); return nil; } NSMutableDictionary *mDict = [NSMutableDictionary dictionary]; NSDictionary *dictSpKeyDict = [[self class] specialKeysAndReplaceKeys]; NSArray *arrSpecialKeys = [dictSpKeyDict allKeys]; NSArray *arrCustomKeys = [[self class] customAcitonKeys]; NSArray *arrLocalKeys = [[self class] localKeys]; for (unsigned int i = 0; i < count; i++) { objc_property_t property = properties[i]; NSString *key = [NSString stringWithUTF8String:property_getName(property)]; id value = [self valueForKey:key]; if (value) { if ([arrCustomKeys containsObject:key] || [arrLocalKeys containsObject:key]) { continue; } else if ([arrSpecialKeys containsObject:key]) { [mDict addEntriesFromDictionary:@{ dictSpKeyDict[ key ] : value }]; } else { [mDict addEntriesFromDictionary:@{ key : value }]; } } } free(properties); return mDict; } - (NSMutableDictionary *)mutableDictDateToStringForCommit { unsigned int count; objc_property_t *properties = class_copyPropertyList([self class], &count); if (count == 0) { free(properties); return nil; } NSMutableDictionary *mDict = [NSMutableDictionary dictionary]; NSDictionary *dictSpKeyDict = [[self class] specialKeysAndReplaceKeys]; NSArray *arrSpecialKeys = [dictSpKeyDict allKeys]; NSArray *arrCustomKeys = [[self class] customAcitonKeys]; NSArray *arrLocalKeys = [[self class] localKeys]; for (unsigned int i = 0; i < count; i++) { objc_property_t property = properties[i]; NSString *key = [NSString stringWithUTF8String:property_getName(property)]; id value = [self valueForKey:key]; if ([value isKindOfClass:[NSDate class]]) { value = [value httpParameterString]; } if (value) { if ([arrCustomKeys containsObject:key] || [arrLocalKeys containsObject:key]) { continue; } else if ([arrSpecialKeys containsObject:key]) { [mDict addEntriesFromDictionary:@{dictSpKeyDict[key]: value}]; } else { [mDict addEntriesFromDictionary:@{key: value}]; } } } free(properties); return mDict; } - (NSDictionary *)dictForLocalSave { unsigned int count; objc_property_t *properties = class_copyPropertyList([self class], &count); if (count == 0) { free(properties); return nil; } NSMutableDictionary *mDict = [NSMutableDictionary dictionary]; NSDictionary *dictSpKeyDict = [[self class] specialKeysAndReplaceKeys]; NSArray *arrSpecialKeys = [dictSpKeyDict allKeys]; NSArray *arrCustomKeys = [[self class] customAcitonKeys]; for (unsigned int i = 0; i < count; i++) { objc_property_t property = properties[i]; NSString *key = [NSString stringWithUTF8String:property_getName(property)]; id value = [self valueForKey:key]; if (value) { if ([arrCustomKeys containsObject:key]) { continue; } else if ([arrSpecialKeys containsObject:key]) { [mDict addEntriesFromDictionary:@{ dictSpKeyDict[ key ] : value }]; } else { [mDict addEntriesFromDictionary:@{ key : value }]; } } } free(properties); return mDict; } - (void)praseFromJsonDict:(NSDictionary *)dict { [self praseFromJsonDict:dict isNilLeague:YES]; } - (void)praseFromLocalDict:(NSDictionary *)dict { [self praseFromLocalDict:dict isNilLeague:YES]; } - (void)praseFromJsonDict:(NSDictionary *)dict isNilLeague:(BOOL)bIsNilLeague { if (!IsDictObject(dict)) { return; } NSDictionary *dictSpKeyDict = [[self class] specialKeysAndReplaceKeys]; NSArray *arrSpecialKeys = [dictSpKeyDict allKeys]; NSArray *arrCustomKeys = [[self class] customAcitonKeys]; NSArray *arrLocalKeys = [[self class] localKeys]; unsigned int count; objc_property_t *properties = class_copyPropertyList([self class], &count); for (unsigned int i = 0; i < count; i++) { objc_property_t property = properties[i]; NSString *key = [NSString stringWithUTF8String:property_getName(property)]; NSString *keyInJson = key; if ([arrCustomKeys containsObject:key] || [arrLocalKeys containsObject:key]) { continue; } else if ([arrSpecialKeys containsObject:key]) { keyInJson = dictSpKeyDict[ key ]; } id obj = dict[ keyInJson ]; obj = obj != [NSNull null] ? obj : nil; if (bIsNilLeague) { if (!obj) { NSString *typeAttribute = [[[NSString stringWithUTF8String:property_getAttributes(property)] componentsSeparatedByString:@","] firstObject]; NSString *nsPropertyType = [typeAttribute substringFromIndex:1]; const char *propertyType = [nsPropertyType UTF8String]; if (strcmp(propertyType, @encode(BOOL)) == 0) { obj = @( NO ); } else if (strcmp(propertyType, @encode(unsigned long long)) == 0 || strcmp(propertyType, @encode(long long)) == 0 || strcmp(propertyType, @encode(double)) == 0 || strcmp(propertyType, @encode(float)) == 0 || strcmp(propertyType, @encode(long)) == 0 || strcmp(propertyType, @encode(unsigned long)) == 0 || strcmp(propertyType, @encode(int)) == 0 || strcmp(propertyType, @encode(unsigned int)) == 0 || strcmp(propertyType, @encode(short)) == 0 || strcmp(propertyType, @encode(unsigned short)) == 0) { obj = @( 0 ); } } [self setValue:obj forKey:key]; } else { if (obj) { [self setValue:obj forKey:key]; } } } free(properties); } - (void)praseFromLocalDict:(NSDictionary *)dict isNilLeague:(BOOL)bIsNilLeague { if (!IsDictObject(dict)) { return; } NSDictionary *dictSpKeyDict = [[self class] specialKeysAndReplaceKeys]; NSArray *arrSpecialKeys = [dictSpKeyDict allKeys]; NSArray *arrCustomKeys = [[self class] customAcitonKeys]; unsigned int count; objc_property_t *properties = class_copyPropertyList([self class], &count); for (unsigned int i = 0; i < count; i++) { objc_property_t property = properties[i]; NSString *key = [NSString stringWithUTF8String:property_getName(property)]; NSString *keyInJson = key; if ([arrCustomKeys containsObject:key]) { continue; } else if ([arrSpecialKeys containsObject:key]) { keyInJson = dictSpKeyDict[ key ]; } id obj = dict[ keyInJson ]; obj = obj != [NSNull null] ? obj : nil; if (bIsNilLeague) { if (!obj) { NSString *typeAttribute = [[[NSString stringWithUTF8String:property_getAttributes(property)] componentsSeparatedByString:@","] firstObject]; NSString *nsPropertyType = [typeAttribute substringFromIndex:1]; const char *propertyType = [nsPropertyType UTF8String]; if (strcmp(propertyType, @encode(BOOL)) == 0) { obj = @( NO ); } else if (strcmp(propertyType, @encode(unsigned long long)) == 0 || strcmp(propertyType, @encode(long long)) == 0 || strcmp(propertyType, @encode(double)) == 0 || strcmp(propertyType, @encode(float)) == 0 || strcmp(propertyType, @encode(long)) == 0 || strcmp(propertyType, @encode(unsigned long)) == 0 || strcmp(propertyType, @encode(int)) == 0 || strcmp(propertyType, @encode(unsigned int)) == 0 || strcmp(propertyType, @encode(short)) == 0 || strcmp(propertyType, @encode(unsigned short)) == 0) { obj = @( 0 ); } } [self setValue:obj forKey:key]; } else { if (obj) { [self setValue:obj forKey:key]; } } } free(properties); } #pragma mark - Helper + (NSString *)ValuePlaceholdersWithCount:(NSUInteger)uiCount { NSMutableArray *arrTmp = [NSMutableArray array]; for (NSUInteger i = 0; i < uiCount; i++) { [arrTmp addObject:@"?"]; } return [NSString stringWithFormat:@"(%@)", [arrTmp componentsJoinedByString:@","]]; } @end