|
|
1.1 ! root 1: /* TableDataSource.m ! 2: * ! 3: * This data source reads a flat file table (such as Product.table or Item.table) ! 4: * and generates EOGeneric records that can be passed to an EOController. ! 5: * ! 6: * You may freely copy, distribute, and reuse the code in this example. ! 7: * NeXT disclaims any warranty of any kind, expressed or implied, as to its ! 8: * fitness for any particular use. ! 9: * ! 10: * ! 11: * ! 12: */ ! 13: ! 14: #import <appkit/appkit.h> ! 15: #import <eoaccess/eoaccess.h> ! 16: #import "TableDataSource.h" ! 17: #import "TableDataSourcePrivate.h" ! 18: #import "DetailTableDataSource.h" ! 19: #include <sys/file.h> ! 20: ! 21: ! 22: #define @QUALIFIER_ALL @"*" ! 23: #define @QPROPERTY @"PROPERTY" ! 24: #define @QVALUE @"VALUE" ! 25: ! 26: @implementation TableDataSource ! 27: ! 28: // init a new TableDataSource with an array of EOGeneric records and the path to ! 29: // the table file (the flat file database!). The entity name will be found from a ! 30: // record in the array. ! 31: // Use this initialization method when you have existing records (possibly from ! 32: // a real database) and you want to save them to a file ! 33: - initWithEOGenericRecords:(NSArray *)records tablePath:(NSString *)tablePath { ! 34: NSString *tableFile; ! 35: NSMutableArray *dictArray = [NSMutableArray array]; ! 36: NSDictionary *dict; ! 37: int i; ! 38: ! 39: // We need at least a record to get the entity ! 40: if (![records count]) { ! 41: NSLog(@"<initWithEOGenericRecords:tablePath:> No records"); ! 42: return nil; ! 43: } ! 44: entity = [[records objectAtIndex:0] entity]; ! 45: tableFile=[NSString stringWithFormat:@"%@/%@.table", tablePath, [entity name]]; ! 46: for (i=0; i<[records count]; i++) { ! 47: dict = [[records objectAtIndex:i] valuesForKeys:[entity classPropertyNames]]; ! 48: [dictArray addObject:dict]; ! 49: } ! 50: ! 51: // Save the records to a file as a property-list string ! 52: if (![[[dictArray description] dataUsingEncoding:NSASCIIStringEncoding] ! 53: writeToFile:tableFile atomically:NO]) { ! 54: NSLog(@"<initWithEOGenericRecords:tablePath:> DataSource %@ failed to save", [entity name]); ! 55: return nil; ! 56: } ! 57: // Call the designated initialization method ! 58: else return [self initWithEntity:entity tablePath:tablePath]; ! 59: ! 60: } ! 61: ! 62: // The designated initialization method for TableDataSource ! 63: // Pick an entity in the model, then initialize the source from a flat file that ! 64: // contains a string representation of an array of dictionaries. ! 65: // Note that unlike the EODatabaseSataSource, this dataSource has a "state". ! 66: // It cashes the eos (dictionaries) that it read from the file and also maintains ! 67: // a few hash tables to speed up fetch operations... ! 68: // Although the state of the TableDataSource is an array of dictionaries (I call it ! 69: // the snapshot) the TabledataSource will copy these dictionaries into an array of ! 70: // EOGeneric records before handing them to a controller... ! 71: - initWithEntity:(EOEntity *)anEntity tablePath:(NSString *)tablePath { ! 72: NSString *primaryKey; ! 73: NSArray *relations; ! 74: NSMutableArray *qualifiers = [NSMutableArray array]; ! 75: ! 76: [super init]; ! 77: entity = [anEntity retain]; ! 78: primaryKey = [(EOAttribute *)[[entity primaryKeyAttributes] objectAtIndex:0] name]; ! 79: relations = [entity relationships]; ! 80: detailSources = [[NSMutableArray array] retain]; ! 81: return [self initWithTable:tablePath primaryKey:primaryKey qualifierKeys:qualifiers]; ! 82: } ! 83: ! 84: // The new NSObject world, I guess ! 85: - (void)dealloc { ! 86: [table autorelease]; ! 87: [eos autorelease]; ! 88: [lookupTables autorelease]; ! 89: [qualifier autorelease]; ! 90: [uniqueKey autorelease]; ! 91: [detailSources autorelease]; ! 92: [entity autorelease]; ! 93: [orderByKey autorelease]; ! 94: [super dealloc]; ! 95: } ! 96: ! 97: // The path to persistent storage, here to the "table" file... ! 98: - (NSString *)tablePath { ! 99: return table; ! 100: } ! 101: ! 102: // The other one... ! 103: - setTablePath:(NSString *)aPath { ! 104: [table autorelease]; ! 105: table = [aPath retain]; ! 106: return self; ! 107: } ! 108: ! 109: // The supported entity, we got it from the EOmodel at initialization time ! 110: - (EOEntity *)entity { ! 111: return entity; ! 112: } ! 113: ! 114: // A custom way (meaning different from the EOF way) to qualify the source for a given property name and value ! 115: // The equivalent SQL is: WHERE( key = value) ! 116: - qualifyForProperty:(NSString *)key andValue:value { ! 117: NSMutableDictionary *qual = [NSMutableDictionary dictionary]; ! 118: ! 119: if (![[self keys] containsObject:key]) { ! 120: NSLog(@"%@ is not a valid property of entity %@", key, [entity name]); ! 121: qual=nil; ! 122: } ! 123: else { ! 124: [qual setObject:key forKey:@QPROPERTY]; ! 125: [qual setObject:value forKey:@QVALUE]; ! 126: } ! 127: return [self setQualifier:qual]; ! 128: } ! 129: ! 130: // Qualify for all records ! 131: - setEntityQualifier { ! 132: NSMutableDictionary *qual =[NSMutableDictionary dictionary]; ! 133: [qual setObject:uniqueKey forKey:@QPROPERTY]; ! 134: [qual setObject:@QUALIFIER_ALL forKey:@QVALUE]; ! 135: return [self setQualifier:qual]; ! 136: } ! 137: ! 138: // Set the qualifier before the fetch ! 139: - setQualifier:(NSMutableDictionary *)newQualifier { ! 140: [qualifier autorelease]; ! 141: if (newQualifier) qualifier = [newQualifier retain]; ! 142: else qualifier=nil; ! 143: //NSLog(@"New qualifier %@", [(NSMutableDictionary *)qualifier description]); ! 144: return self; ! 145: } ! 146: ! 147: - setEmptySetQualifier { ! 148: [qualifier autorelease]; ! 149: qualifier=nil; ! 150: return self; ! 151: } ! 152: ! 153: - (BOOL)orderBy:(NSString *)key { ! 154: if (![[self keys] containsObject:key]) return NO; ! 155: else { ! 156: [orderByKey autorelease]; ! 157: orderByKey = [key retain]; ! 158: [eos sortUsingFunction:eoSort context:orderByKey]; ! 159: return YES; ! 160: } ! 161: } ! 162: ! 163: - setOrderDescendantSources:(BOOL)aFlag { ! 164: orderDescendantSources=aFlag; ! 165: return self; ! 166: } ! 167: ! 168: // ************************************* DATASOURCE PROTOCOL ****************/ ! 169: // The class property names (keys for the dictionary or EOGenericRecord) ! 170: - (NSArray *)keys { ! 171: NSArray *attrs = [entity attributes]; ! 172: NSMutableArray *keys = [NSMutableArray array]; ! 173: int i; ! 174: ! 175: for (i=0; i<[attrs count]; i++) { ! 176: EOAttribute *at = [attrs objectAtIndex:i]; ! 177: [keys addObject:[at name]]; ! 178: } ! 179: return keys; ! 180: } ! 181: ! 182: - createObject{ ! 183: NSNumber *newId=[self findNextPrimaryKey]; ! 184: NSMutableDictionary *newObject=[[NSMutableDictionary alloc] init]; ! 185: ! 186: if (!newId) return nil; ! 187: else { ! 188: NSArray *allKeys = [self keys]; ! 189: NSArray *relations = [entity relationships]; ! 190: int i, count; ! 191: ! 192: for (i=0, count=[allKeys count]; i<count; i++) { ! 193: id key = [allKeys objectAtIndex:i]; ! 194: [newObject setObject:@"" forKey:key]; ! 195: } ! 196: for (i=0, count=[relations count]; i<count; i++) { ! 197: EORelationship *rel = [relations objectAtIndex:i]; ! 198: if ([rel isToMany]) [newObject setObject:[newId description] forKey:[rel name]]; ! 199: } ! 200: [newObject setObject:[newId description] forKey:uniqueKey]; ! 201: return newObject; ! 202: } ! 203: } ! 204: ! 205: - (BOOL)insertObject:object { ! 206: NSNumber *objectId=[object objectForKey:uniqueKey]; ! 207: NSArray *allKeys; ! 208: int i; ! 209: NSMutableDictionary *newObject; ! 210: ! 211: if (![self isValidNewPrimaryKey:objectId]) { ! 212: NSLog(@"DataSource cannot insert; primary key is invalid: %@", [(NSString *)objectId description]); ! 213: return NO; ! 214: } ! 215: ! 216: newObject=[NSMutableDictionary dictionary]; ! 217: allKeys = [self keys]; ! 218: for (i=0; i<[allKeys count]; i++) { ! 219: id key = [allKeys objectAtIndex:i]; ! 220: id value = [object objectForKey:key]; ! 221: if ([value isEqual:[EONull null]]) value=@""; ! 222: [newObject setObject:value forKey:key]; ! 223: } ! 224: ! 225: [eos addObject:newObject]; ! 226: if (orderByKey) [eos sortUsingFunction:eoSort context:orderByKey]; ! 227: [self modifyLookupTables]; ! 228: return YES; ! 229: } ! 230: ! 231: - (BOOL)deleteAllObjects { ! 232: int i; ! 233: for (i=[eos count]-1; i>=0; i--) { ! 234: [eos removeObject:[eos objectAtIndex:i]]; ! 235: } ! 236: [self modifyLookupTables]; ! 237: return YES; ! 238: } ! 239: ! 240: - (BOOL)deleteObject:object { ! 241: id primaryKeyValue = [object objectForKey:uniqueKey]; ! 242: NSMutableArray *deletes=[[lookupTables objectForKey:uniqueKey] objectForKey:primaryKeyValue]; ! 243: int i; ! 244: ! 245: if ( !deletes || ![deletes count]) return NO; ! 246: for (i=0; i<[deletes count]; i++) { ! 247: [eos removeObject:[deletes objectAtIndex:i]]; ! 248: } ! 249: [self modifyLookupTables]; ! 250: return YES; ! 251: } ! 252: ! 253: - (BOOL)updateObject:object { ! 254: id primaryKeyValue = [object objectForKey:uniqueKey]; ! 255: NSMutableArray *update=[[lookupTables objectForKey:uniqueKey] objectForKey:primaryKeyValue]; ! 256: NSArray *allKeys; ! 257: NSMutableDictionary *eo; ! 258: int i; ! 259: NSString *key; ! 260: id value; ! 261: ! 262: if (!update || [update count]!=1) return NO; ! 263: allKeys = [self keys]; ! 264: eo = [update objectAtIndex:0]; ! 265: for (i=0; i<[allKeys count]; i++) { ! 266: key = [allKeys objectAtIndex:i]; ! 267: value = [object objectForKey:key]; ! 268: if (!value || [value isEqual:[EONull null]] ) value=@""; ! 269: [eo setObject:value forKey:key]; ! 270: } ! 271: if (orderByKey) [eos sortUsingFunction:eoSort context:orderByKey]; ! 272: [self modifyLookupTables]; ! 273: return YES; ! 274: } ! 275: ! 276: - (NSArray *)fetchObjects { ! 277: NSArray *records; ! 278: NSString *qualifierPropertyKey = [qualifier objectForKey:@QPROPERTY]; ! 279: NSString *qualifierPropertyValue = [qualifier objectForKey:@QVALUE]; ! 280: ! 281: if (qualifierPropertyKey && qualifierPropertyValue) { ! 282: ! 283: if ( [qualifierPropertyValue isEqual:@QUALIFIER_ALL] ) { ! 284: records =[self eosArrayToGenericRecordsArray:eos]; ! 285: } ! 286: else { ! 287: NSMutableDictionary *hash = [lookupTables objectForKey:qualifierPropertyKey]; ! 288: if (!hash) { ! 289: NSLog(@"No lookup table for qualifier key %@", qualifierPropertyKey); ! 290: records =nil; ! 291: } ! 292: else { ! 293: NSArray *qualifiedEos=[hash objectForKey:qualifierPropertyValue]; ! 294: if (!qualifiedEos || ![qualifiedEos count]) { ! 295: //NSLog(@"No qualified eos where %@=%@", qualifierPropertyKey, [(NSString *)qualifierPropertyValue description]); ! 296: records =nil; ! 297: } ! 298: records =[self eosArrayToGenericRecordsArray:qualifiedEos]; ! 299: } ! 300: ! 301: } ! 302: } ! 303: else { ! 304: //NSLog(@"nil or uncomplete qualifier %@", [(NSMutableDictionary *)qualifier description]); ! 305: records = [NSArray array]; ! 306: } ! 307: //NSLog(@"TABLE %@ - FETCHED OBJECTS: %d", [entity name], [records count]); ! 308: return records; ! 309: } ! 310: ! 311: // This is where we save the eos to persistent storage (COMMIT in SQL) ! 312: - (BOOL)saveObjects{ ! 313: NSString *tableFile=[NSString stringWithFormat:@"%@/%@.table", table, [entity name]]; ! 314: if (![[[eos description] dataUsingEncoding:NSASCIIStringEncoding] ! 315: writeToFile:tableFile atomically:NO]) { ! 316: NSString *emess; ! 317: ! 318: if ( (!access([tableFile cString], F_OK)) && (access([tableFile cString], W_OK)) ) { ! 319: int ans; ! 320: emess = [NSString stringWithFormat:@"\'%@\' data source cannot save changes. You do not have write permission on \'%@\'. Do you want to overwrite?", [entity name], tableFile]; ! 321: ans=NXRunAlertPanel("ERROR", [emess cString], "Overwrite", "Cancel", NULL); ! 322: if (ans==NX_ALERTDEFAULT) return [self forceSaveObjects]; ! 323: else return NO; ! 324: } ! 325: else return NO; ! 326: } ! 327: else return YES; ! 328: } ! 329: ! 330: - (BOOL)canDelete { ! 331: return YES; ! 332: } ! 333: ! 334: // Not implemented yet... ! 335: // Coerce a value to the appropriate type. ! 336: // This method should convert to either an NSNumber, NSString, NSData, ! 337: // a custom type, or nil. The value return by this method may be safely ! 338: // passed to an EO via takeValuesFromDictionary:. This method is used ! 339: // by controllers to coerce values supplied from associations before ! 340: // those values are passed on to the EOs. ! 341: - coerceValue: value forKey: (NSString *)key { ! 342: // sorry, another day , maybe... ! 343: return value; ! 344: } ! 345: ! 346: // ****************************** MASTER DATASOURCE PROTOCOL ****************/ ! 347: // What we are doing here is closer to a master-peer than a master-detail setup. ! 348: // We are creating the detail dataSource and handing it to the association. ! 349: // This new detail dataSource will be attached to the detail controller automatically ! 350: // From there, the detail controller and its DetailTableDataSource will be on their own ! 351: // Also notice than in an EOF master-detail setup, a master eo (meaning an eo ! 352: // produced by the master source) "carries" its detail eos. In other word if one ! 353: // writes [masterEo objectForKey:masterDetailRelationshipKey], the master eo will ! 354: // return an array full of detail eos (assuming that the master detail relationship ! 355: // was set as a class property name in EOModeler). Although it could, the TableDataSource ! 356: // does not support that feature... ! 357: - (id <EOQualifiableDataSources>)dataSourceQualifiedByKey:(NSString *)key { ! 358: EORelationship *rel = [entity relationshipNamed:key]; ! 359: EOEntity *detailEntity=[rel destinationEntity]; ! 360: DetailTableDataSource *detailSource; ! 361: ! 362: if (!rel) return nil; ! 363: if (!detailEntity) return nil; ! 364: detailSource = [[(DetailTableDataSource *)[DetailTableDataSource alloc] initWithMasterDataSource:self entity:detailEntity] autorelease]; ! 365: if (orderByKey && orderDescendantSources) [detailSource orderBy:orderByKey]; ! 366: ! 367: // We cash it (I had some grandiose plane, unused today!!) ! 368: [detailSources addObject:detailSource]; ! 369: return detailSource; ! 370: } ! 371: ! 372: // ***************************************************************************/ ! 373: ! 374: // a convenience method to fetch all the objects in the entity ! 375: - (NSArray *)fetchAllObjects { ! 376: NSMutableDictionary *qual =[NSMutableDictionary dictionary]; ! 377: [qual setObject:uniqueKey forKey:@QPROPERTY]; ! 378: [qual setObject:@QUALIFIER_ALL forKey:@QVALUE]; ! 379: [self setQualifier:qual]; ! 380: return [self fetchObjects]; ! 381: } ! 382: ! 383: // a convenience fetch method ! 384: - objectForPrimaryKey:value { ! 385: id hash = [lookupTables objectForKey:uniqueKey]; ! 386: NSArray *objects = [hash objectForKey:value]; ! 387: id object; ! 388: EOGenericRecord *record; ! 389: NSMutableDictionary *primaryKeyDictionary; ! 390: NSString *key; ! 391: id copy; ! 392: int count, j; ! 393: NSArray *allKeys = [self keys]; ! 394: ! 395: if ([objects count]!=1) return nil; ! 396: object = [objects objectAtIndex:0]; ! 397: primaryKeyDictionary=[NSMutableDictionary dictionary]; ! 398: [primaryKeyDictionary setObject:value forKey:uniqueKey]; ! 399: record = [[EOGenericRecord alloc] initWithPrimaryKey:primaryKeyDictionary ! 400: entity:entity]; ! 401: for (j=0, count=[allKeys count]; j<count; j++) { ! 402: key = [allKeys objectAtIndex:j]; ! 403: copy = [[object objectForKey:key] copy]; ! 404: [record setObject:[copy autorelease] forKey:key]; ! 405: } ! 406: return [record autorelease]; ! 407: } ! 408: ! 409: - addLookupTableForKey:(NSString *)key { ! 410: NSMutableDictionary *hash; ! 411: ! 412: hash = [self createLookupTableForQualifierKey:key]; ! 413: [lookupTables setObject:hash forKey:key]; ! 414: return self; ! 415: } ! 416: ! 417: ! 418: ! 419: @end ! 420: ! 421: // ************************************* Private Category ****************/ ! 422: @implementation TableDataSource (Private) ! 423: ! 424: // This initialization method is called by initWithEntity:tablePath: ! 425: // It sets the key for the primary key and creates a key->array of objects hash ! 426: // tables for each element of the qualifiers array. ! 427: - initWithTable:(NSString *)tablePath primaryKey:(NSString *)primaryKey qualifierKeys:(NSArray *)qualifiers { ! 428: ! 429: // Create snapshot from table (eos) ! 430: table = [tablePath retain]; ! 431: if (![self createSnapshot]) return nil; ! 432: ! 433: // Create a dictionary of hash tables for query (key is like @"ID", value is a lookup table) ! 434: uniqueKey = [primaryKey retain]; ! 435: if ( !([self createLookupTables:qualifiers]) ) return nil; ! 436: ! 437: // set qualifier to select everything ! 438: // To be clean, we should have a qualifier Class, oh well...it is a rush job ! 439: // after all ! 440: orderByKey=nil; ! 441: orderDescendantSources = NO; ! 442: qualifier = [[NSMutableDictionary alloc] init]; ! 443: [qualifier setObject:uniqueKey forKey:@QPROPERTY]; ! 444: [qualifier setObject:@QUALIFIER_ALL forKey:@QVALUE]; ! 445: ! 446: return self; ! 447: } ! 448: ! 449: // The flat file is read here and the objects that have been retrieved are cashed ! 450: // in an instance variable (of class NSMutableArray) called eos ! 451: - createSnapshot { ! 452: NSString *tableFile=[NSString stringWithFormat:@"%@/%@", table, [entity externalName]]; ! 453: NSString *tableString; ! 454: NSArray *nonMutableEos; ! 455: ! 456: // read the table file that should be in a property list format ! 457: // Isn't foundation great or what?!!! ! 458: if ( !(tableString=[[NSString alloc] initWithContentsOfFile:tableFile]) || ! 459: !(nonMutableEos = [tableString propertyList]) ) { ! 460: NSLog(@"<createSnapshot> Cannot init from table %@", tableFile); ! 461: return nil; ! 462: } ! 463: // Make deep mutable copy ! 464: else { ! 465: NSEnumerator *eoEnumerator = [nonMutableEos objectEnumerator]; ! 466: NSDictionary *nonMutableEo; ! 467: ! 468: eos = [[NSMutableArray alloc] init]; ! 469: while ( nonMutableEo = [eoEnumerator nextObject] ) { ! 470: [eos addObject: [[nonMutableEo mutableCopy] autorelease]]; ! 471: } ! 472: } ! 473: return self; ! 474: } ! 475: ! 476: // Create the lookup tables (to speed up fetch operations) ! 477: // The qualifiers array contains the property names that we should hash on ! 478: // a lookup table is in the form key = "a property key" --> value = an array of ! 479: // dictionaries... ! 480: - createLookupTables:(NSArray *)qualifiers { ! 481: int i, count; ! 482: NSMutableDictionary *hash; ! 483: NSString *qualifierKey; ! 484: ! 485: lookupTables = [[NSMutableDictionary alloc] init]; ! 486: hash = [self createLookupTableForQualifierKey:uniqueKey]; ! 487: [lookupTables setObject:hash forKey:uniqueKey]; ! 488: ! 489: for (i=0, count=[qualifiers count]; i<count; i++) { ! 490: ! 491: qualifierKey = [qualifiers objectAtIndex:i]; ! 492: if ( ![lookupTables objectForKey:qualifierKey] ) { ! 493: //NSLog(@"Creating lookup table for qualifierKey %@", qualifierKey); ! 494: if ( !(hash = [self createLookupTableForQualifierKey:qualifierKey]) ) return nil; ! 495: [lookupTables setObject:hash forKey:qualifierKey]; ! 496: } ! 497: } ! 498: return self; ! 499: } ! 500: ! 501: // Create a lookup table for a given property name (the key) ! 502: - createLookupTableForQualifierKey:(NSString *)key { ! 503: ! 504: NSMutableDictionary *hash; ! 505: int i, count; ! 506: NSDictionary *eo; ! 507: NSMutableArray *values; ! 508: id value; ! 509: ! 510: hash=[NSMutableDictionary dictionary]; ! 511: ! 512: for (i=0, count=[eos count]; i<count; i++) { ! 513: ! 514: eo = [eos objectAtIndex:i]; ! 515: ! 516: // get the property value for the qualifier key ! 517: if ( !(value = [eo objectForKey:key]) ) { ! 518: NSLog(@"<createLookupTableForQualifierKey:> eos dictionary does not respond to qualifier key %@", key); ! 519: return nil; ! 520: } ! 521: ! 522: // It is the first time that we encounter an eo such that propertyValue(key) = value ! 523: if ( !(values = [hash objectForKey:value]) ) { ! 524: values = [NSMutableArray array]; ! 525: [values addObject:eo]; ! 526: [hash setObject:values forKey:[(NSObject *)value description]]; ! 527: } ! 528: // We have already found an eo such that propertyValue(key) = value ! 529: else { ! 530: [values addObject:eo]; ! 531: } ! 532: } ! 533: //NSLog(@"HASH FOR KEY %@\n%@", key, [(NSDictionary *)hash description]); ! 534: return hash; ! 535: } ! 536: ! 537: // After insert, delete or update opeations, the lookup tables must be refreshed ! 538: - modifyLookupTables { ! 539: NSArray *allKeys=[lookupTables allKeys]; ! 540: int i, count; ! 541: NSString *key; ! 542: NSMutableDictionary *hash; ! 543: ! 544: for (i=0, count=[allKeys count]; i<count; i++) { ! 545: key = [allKeys objectAtIndex:i]; ! 546: [lookupTables removeObjectForKey:key]; ! 547: hash = [self createLookupTableForQualifierKey:key]; ! 548: [lookupTables setObject:hash forKey:key]; ! 549: } ! 550: return self; ! 551: } ! 552: ! 553: // a method to transform an array of eos (state of the TableDataSource) into ! 554: // an array of EOGeneric records that we can hand to the outside worls... ! 555: - (NSMutableArray *)eosArrayToGenericRecordsArray:(NSArray *)eoArray { ! 556: NSMutableArray *records = [NSMutableArray array]; ! 557: int i, eoCount; ! 558: ! 559: for (i=0, eoCount=[eoArray count]; i<eoCount; i++) { ! 560: NSMutableDictionary *eo; ! 561: NSMutableDictionary *primaryKey=[NSMutableDictionary dictionary]; ! 562: EOGenericRecord *record; ! 563: NSArray *allKeys; ! 564: NSArray *relations; ! 565: int j, count; ! 566: ! 567: eo = [eoArray objectAtIndex:i]; ! 568: allKeys = [self keys]; ! 569: [primaryKey setObject:[eo objectForKey:uniqueKey] forKey:uniqueKey]; ! 570: record = [[[EOGenericRecord alloc] initWithPrimaryKey:primaryKey entity:entity] autorelease]; ! 571: for (j=0, count=[allKeys count]; j<count; j++) { ! 572: NSString *key = [allKeys objectAtIndex:j]; ! 573: id copy; ! 574: ! 575: copy = [[eo objectForKey:key] copy]; ! 576: [record setObject:[copy autorelease] forKey:key]; ! 577: } ! 578: relations = [entity relationships]; ! 579: ! 580: // We support master-details but not flattened attribute for now!!! ! 581: for (j=0, count=[relations count]; j<count; j++) { ! 582: EORelationship *rel = [relations objectAtIndex:j]; ! 583: id copy; ! 584: ! 585: if ([rel isToMany]) { ! 586: copy = [[eo objectForKey:uniqueKey] copy]; ! 587: [record setObject:[copy autorelease] forKey:[rel name]]; ! 588: } ! 589: ! 590: } ! 591: [records addObject:record]; ! 592: } ! 593: return records; ! 594: } ! 595: ! 596: // Unlike the EODatabaseDataSource, when a TableDataSource creates a new object ! 597: // It sets the primary key value for the created object. ! 598: // This method computes the next available primary key (the next in the sequence) ! 599: - (NSNumber *)findNextPrimaryKey { ! 600: int i, max=-1; ! 601: ! 602: for (i=0; i<[eos count]; i++) { ! 603: id eo; ! 604: int current; ! 605: ! 606: eo = [eos objectAtIndex:i]; ! 607: current = [[eo objectForKey:uniqueKey] intValue]; ! 608: if (current > max) max=current; ! 609: } ! 610: return [NSNumber numberWithInt:max+1]; ! 611: } ! 612: ! 613: // make sure the the primary key of a new object handed for insertion has a valid primary key ! 614: // (in this case, valid means not used by a record stored in the table) ! 615: - (BOOL)isValidNewPrimaryKey:(NSNumber *)number { ! 616: int i; ! 617: ! 618: for (i=0; i<[eos count]; i++) { ! 619: id eo; ! 620: ! 621: eo = [eos objectAtIndex:i]; ! 622: if ([number isEqual:[eo objectForKey:uniqueKey]]) return NO; ! 623: } ! 624: return YES; ! 625: } ! 626: ! 627: - (BOOL)forceSaveObjects { ! 628: NSString *tableFile=[NSString stringWithFormat:@"%@/%@.table", table, [entity name]]; ! 629: NSString *emess=@"Cannot overwrite; changes will not be saved..."; ! 630: NSString *cmd; ! 631: ! 632: cmd = [NSString stringWithFormat:@"mv %@ %@.otto", tableFile, tableFile]; ! 633: if (system([cmd cString])) { ! 634: NXRunAlertPanel("ERROR", [emess cString], "OK", NULL, NULL); ! 635: return NO; ! 636: } ! 637: cmd = [NSString stringWithFormat:@"cp %@.otto %@; ", tableFile, tableFile]; ! 638: if (system([cmd cString])) { ! 639: NXRunAlertPanel("ERROR", [emess cString], "OK", NULL, NULL); ! 640: return NO; ! 641: } ! 642: cmd = [NSString stringWithFormat:@"rm -f %@.otto; chmod u+w %@", tableFile, tableFile]; ! 643: system([cmd cString]); ! 644: ! 645: if (![[[eos description] dataUsingEncoding:NSASCIIStringEncoding] ! 646: writeToFile:tableFile atomically:NO]) return NO; ! 647: else return YES; ! 648: ! 649: } ! 650: ! 651: int eoSort(id eo1, id eo2, void *context) { ! 652: NSString *key=(NSString *)context; ! 653: NSString *val1, *val2; ! 654: ! 655: if (!key) return NSOrderedSame; ! 656: val1 = [eo1 objectForKey:key]; ! 657: val2 = [eo2 objectForKey:key]; ! 658: if (!val1 || !val2) return NSOrderedSame; ! 659: else return [val1 compare:val2]; ! 660: } ! 661: ! 662: ! 663: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.