home *** CD-ROM | disk | FTP | other *** search
/ OpenStep (Enterprise) / OpenStepENTCD.toast / OEDEV / EODEV.Z / FlatFileContext.m < prev    next >
Encoding:
Text File  |  1996-08-23  |  10.9 KB  |  379 lines

  1. /*
  2.         Copyright (c) 1996, NeXT Software, Inc.
  3.         All rights reserved.
  4.  
  5.         You may freely copy, distribute and reuse the code in this example.
  6.         NeXT disclaims any warranty of any kind, expressed or implied,
  7.         as to its fitness for any particular use.
  8. */
  9. #import "FlatFileContext.h"
  10. #import "FlatFileAdaptor.h"
  11. #import "FlatFileChannel.h"
  12. #import <FileScanning.h>
  13.  
  14. #define DataKey           @"data"
  15. #define ColumnInfoKey     @"infos"
  16. #define ColumnIndexMapKey @"nameToIndexMap"
  17. #define ColumnHeaderKey   @"columnHeaders"
  18.  
  19. @interface FlatFileContext (FlatFileContextPrivate)
  20.  
  21. - (NSMutableDictionary *)infoForTable:(NSString *)tableName;
  22. - (NSMutableDictionary *)newInfoForTable:(NSString *)tableName;
  23. - (NSDictionary *)columnInfoFromHeader:(char *)header length:(int)length;
  24. - (NSMutableData *)newDataForTable:(NSString *)tableName;
  25.  
  26. - (void)stripHeadersFromData:(NSMutableData *)data;
  27.  
  28. - (void)saveDirtyTables;
  29. - (void)dropDirtyTables;
  30.  
  31. @end
  32.  
  33. @implementation FlatFileContext
  34.  
  35. - initWithFlatFileAdaptor:(FlatFileAdaptor *)adaptor
  36. {
  37.     [super initWithAdaptor:adaptor];
  38.     return self;
  39. }
  40.  
  41. - (void)dealloc
  42. {
  43.     [tableNameToInfoMap release];
  44.     [dirtyTables release];
  45.  
  46.     [super dealloc];
  47. }
  48.  
  49. - (void)beginTransaction
  50. {
  51.     if ([self transactionNestingLevel]) {
  52.         [NSException raise:NSInternalInconsistencyException format:@"%@ -- %@ 0x%x: attempted to begin a transaction within a transaction", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self];
  53.     }
  54.  
  55.     if (_delegateRespondsTo.shouldBegin) {
  56.         if (![_delegate adaptorContextShouldBegin:self]) return;
  57.     }
  58.  
  59.     // a normal adaptor would actually do something to begin the transaction here
  60.     
  61.     [self transactionDidBegin];
  62.  
  63.     if (_delegateRespondsTo.didBegin)
  64.         [_delegate adaptorContextDidBegin:self];
  65. }
  66.  
  67. - (void)commitTransaction
  68. {
  69.     if (![self transactionNestingLevel])
  70.         [NSException raise:NSInternalInconsistencyException format:@"%@ -- %@ 0x%x:illegal attempt to commit a transaction when there are none in progress", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self];
  71.  
  72.     if (_delegateRespondsTo.shouldCommit) {
  73.         if (![_delegate adaptorContextShouldCommit:self]) return;
  74.     }
  75.  
  76.     [self saveDirtyTables];
  77.     [self transactionDidCommit];
  78.  
  79.     if (_delegateRespondsTo.didCommit)
  80.         [_delegate adaptorContextDidCommit:self];
  81. }
  82.  
  83. - (void)rollbackTransaction
  84. {
  85.     if (![self transactionNestingLevel])
  86.         [NSException raise:NSInternalInconsistencyException format:@"%@ -- %@ 0x%x: attempted to commit a transaction when there are none in progress", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self];
  87.  
  88.     if (_delegateRespondsTo.shouldRollback) {
  89.         if (![_delegate adaptorContextShouldRollback:self]) return;
  90.     }
  91.  
  92.     [self dropDirtyTables];
  93.     [self transactionDidRollback];
  94.  
  95.     if (_delegateRespondsTo.didRollback)
  96.         [_delegate adaptorContextDidRollback:self];
  97. }
  98.  
  99. - (BOOL)canNestTransactions
  100. {
  101.     return NO;
  102. }
  103.  
  104. - (EOAdaptorChannel *)createAdaptorChannel
  105. {
  106.     return [[[FlatFileChannel alloc] initWithFlatFileContext:self] autorelease];
  107. }
  108.  
  109. - (BOOL)autoBeginTransaction {
  110.     if (![self transactionNestingLevel]) {
  111.         [self beginTransaction];
  112.         didAutoBegin = YES;
  113.         return YES;
  114.     }
  115.     return NO;
  116. }
  117.  
  118. - (BOOL)autoCommitTransaction {
  119.     if (didAutoBegin) {
  120.         [self commitTransaction];
  121.         didAutoBegin = NO;
  122.         return YES;
  123.     }
  124.     return NO;
  125. }
  126.  
  127. - (NSString *)path
  128. {
  129.     NSDictionary *dict = [[self adaptor] connectionDictionary];
  130.     return [dict objectForKey:FlatFilePathKey];
  131. }
  132.  
  133. - (NSString *)rowSeparator
  134. {
  135.     NSString *value = [[[self adaptor] connectionDictionary] objectForKey:FlatFileRowSeparatorKey];
  136.     return value ? value : [FlatFileAdaptor defaultRowSeparator];
  137. }
  138.  
  139. - (NSString *)columnSeparator
  140. {
  141.     NSString *value = [[[self adaptor] connectionDictionary] objectForKey:FlatFileColumnSeparatorKey];
  142.     return value ? value : [FlatFileAdaptor defaultColumnSeparator];
  143. }
  144.  
  145. - (BOOL)useColumnHeaders
  146. {
  147.     NSString *value = [[[self adaptor] connectionDictionary] objectForKey:FlatFileUseHeadersKey];
  148.     return value ? [value isEqualToString:@"Y"] : YES;
  149. }
  150.  
  151. - (NSMutableData *)dataForTable:(NSString *)tableName
  152. {
  153.     NSMutableDictionary *tableInfo = [self infoForTable:tableName];
  154.     NSMutableData *data;
  155.  
  156.     data = [tableInfo objectForKey:DataKey];
  157.     if (!data) {
  158.         data = [self newDataForTable:tableName];
  159.         [tableInfo setObject:data forKey:DataKey];
  160.     }
  161.     return data;
  162. }
  163.  
  164. - (NSMutableData *)newDataForTable:(NSString *)tableName
  165. {
  166.     NSMutableData *data;
  167.     NSString *fullPath;
  168.  
  169.     fullPath = [[self path] stringByAppendingPathComponent:tableName];
  170.     data = [NSMutableData dataWithContentsOfFile:fullPath];
  171.  
  172.     if (!data)
  173.         [NSException raise:EOGeneralAdaptorException format:@"can't read %@", fullPath];
  174.  
  175.     if ([self useColumnHeaders])
  176.         [self stripHeadersFromData:data];
  177.  
  178.     return data;
  179. }
  180.  
  181. - (NSArray *)columnInfosForTable:(NSString *)tableName
  182. {
  183.     return [[self infoForTable:tableName] objectForKey:ColumnInfoKey];
  184. }
  185.  
  186. - (NSDictionary *)columnIndexMapForTable:(NSString *)tableName
  187. {
  188.     return [[self infoForTable:tableName] objectForKey:ColumnIndexMapKey];
  189. }
  190.  
  191. - (NSMutableDictionary *)infoForTable:(NSString *)tableName
  192. {
  193.     NSMutableDictionary *tableInfo = [tableNameToInfoMap objectForKey:tableName];
  194.  
  195.     if (!tableInfo) {
  196.         tableInfo = [self newInfoForTable:tableName];
  197.         if (tableInfo) {
  198.             if (!tableNameToInfoMap)
  199.                 tableNameToInfoMap = [[NSMutableDictionary new] retain];
  200.  
  201.             [tableNameToInfoMap setObject:tableInfo forKey:tableName];
  202.         }
  203.     }
  204.     return tableInfo;
  205. }
  206.  
  207. - (NSMutableDictionary *)newInfoForTable:(NSString *)tableName
  208. {
  209.     NSString *fullPath, *columnName;
  210.     NSDictionary *columnInfo;
  211.     NSMutableDictionary *tableInfo, *columnIndexMap;
  212.     NSData *columnHeaders;
  213.     NSMutableData *data;
  214.     NSMutableArray *columnInfos;
  215.     char *row, *currentHeader, *nextHeader, *end, *delimiter;
  216.     int length, index = 0;
  217.     BOOL useHeaders = [self useColumnHeaders];
  218.     
  219.     fullPath = [[self path] stringByAppendingPathComponent:tableName];
  220.     data = [NSMutableData dataWithContentsOfFile:fullPath];
  221.  
  222.     NS_DURING;
  223.     
  224.     if (!data)
  225.         [NSException raise:EOGeneralAdaptorException format:@"can't read %@", fullPath];
  226.  
  227.     row = (char *)[data bytes];
  228.     end = row + [data length] - 1;
  229.     delimiter = (char *)[[self rowSeparator] cString];
  230.  
  231.     nextHeader = FFNextTokenIn(row, &length, end, delimiter);
  232.  
  233.     if (!length)
  234.         [NSException raise:EOGeneralAdaptorException format:@"can't derive metadata for %@", fullPath];
  235.  
  236.     if (useHeaders)
  237.         columnHeaders = [NSData dataWithBytes:row length:(nextHeader) ? nextHeader - row : end - row + 1];
  238.  
  239.     end = row + length - 1;
  240.     currentHeader = row;
  241.     
  242.     columnInfos = [NSMutableArray array];
  243.     columnIndexMap = [NSMutableDictionary dictionary];
  244.     delimiter = (char *)[[self columnSeparator] cString];
  245.     
  246.     while (currentHeader) {
  247.         nextHeader = FFNextTokenIn(currentHeader, &length, end, delimiter);
  248.         if (useHeaders) {
  249.             columnInfo = [self columnInfoFromHeader:currentHeader length:length];
  250.             columnName = [columnInfo objectForKey:ColumnNameKey];
  251.         } else {
  252.             columnName = [NSString stringWithFormat:@"Attribute%d", index];
  253.             columnInfo = [NSDictionary dictionaryWithObjectsAndKeys:columnName, ColumnNameKey, @"String", ColumnTypeKey, nil];
  254.         }
  255.         [columnIndexMap setObject:[NSNumber numberWithInt:index++] forKey:columnName];
  256.         [columnInfos addObject:columnInfo];
  257.  
  258.         currentHeader = nextHeader;
  259.     }
  260.  
  261.     tableInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:data, DataKey, columnInfos, ColumnInfoKey, columnIndexMap, ColumnIndexMapKey, nil];
  262.  
  263.     if (useHeaders) {
  264.         [tableInfo setObject:columnHeaders forKey:ColumnHeaderKey];
  265.         [self stripHeadersFromData:data];
  266.     }
  267.  
  268.     NS_HANDLER;
  269.     tableInfo = nil;
  270.     NS_ENDHANDLER;
  271.  
  272.     return tableInfo;
  273. }
  274.  
  275. - (NSDictionary *)columnInfoFromHeader:(char *)header length:(int)length
  276. {
  277.     NSString *columnName, *columnType;
  278.     char *nextToken, *end = header + length - 1, *delimiter;
  279.  
  280.     delimiter = (char *)[[FlatFileAdaptor columnHeaderSeparator] cString];
  281.     nextToken = FFNextTokenIn(header, &length, end, delimiter);
  282.     columnName = [NSString stringWithCString:header length:length];
  283.  
  284.     if (nextToken) {
  285.         header = nextToken;
  286.         FFNextTokenIn(header, &length, end, delimiter);
  287.         columnType = [NSString stringWithCString:header length:length];
  288.     } else
  289.         columnType = [FlatFileAdaptor defaultExternalType];
  290.  
  291.     return [NSDictionary dictionaryWithObjectsAndKeys:columnName, ColumnNameKey, columnType, ColumnTypeKey, nil];
  292. }
  293.  
  294. - (void)channel:(EOAdaptorChannel *)channel didDirtyTable:(NSString *)table
  295. {
  296.     if (!dirtyTables)
  297.         dirtyTables = [[NSMutableSet new] retain];
  298.  
  299.     [dirtyTables addObject:table];
  300. }
  301.  
  302. - (void)stripHeadersFromData:(NSMutableData *)data
  303. {
  304.     char *firstRow = [data mutableBytes], *nextRow, *end, *delimiter;
  305.     int rowLength, dataLength = [data length];
  306.  
  307.     if (!dataLength)
  308.         return;
  309.  
  310.     delimiter = (char *)[[self rowSeparator] cString];
  311.     end = firstRow + dataLength - 1;
  312.     nextRow = FFNextTokenIn(firstRow, &rowLength, end, delimiter);
  313.  
  314.     if (nextRow) {
  315.         memmove(firstRow, nextRow, 1 + end - nextRow);
  316.         [data setLength:dataLength - (nextRow - firstRow)];
  317.     } else
  318.         [data setLength:0];
  319. }
  320.  
  321. - (void)saveDirtyTables
  322. {
  323.     NSDictionary *tableInfo;
  324.     NSMutableData *fileData, *tableData;
  325.     NSEnumerator *enumerator;
  326.     NSString *path, *tableName, *fullPath, *rowSeparator;
  327.     BOOL useHeaders;
  328.  
  329.     if (!dirtyTables)
  330.         return;
  331.  
  332.     enumerator = [dirtyTables objectEnumerator];
  333.     useHeaders = [self useColumnHeaders];
  334.     rowSeparator = [self rowSeparator];
  335.     path = [self path];
  336.  
  337.     while (tableName = [enumerator nextObject]) {
  338.         tableInfo = [self infoForTable:tableName];
  339.         tableData = [tableInfo objectForKey:DataKey];
  340.         fullPath = [path stringByAppendingPathComponent:tableName];
  341.         
  342.         if (useHeaders) {
  343.             fileData = [[NSMutableData alloc] init];
  344.             [fileData appendData:[tableInfo objectForKey:ColumnHeaderKey]];
  345.             if ([tableData length])
  346.                 [fileData appendData:[tableInfo objectForKey:DataKey]];
  347.         } else
  348.             fileData = tableData;
  349.  
  350.         if (![fileData writeToFile:fullPath atomically:YES])
  351.             [NSException raise:EOGeneralAdaptorException format:@"can't write %@", fullPath];
  352.  
  353.         if (fileData != tableData)
  354.             [fileData release];
  355.     }
  356.  
  357.     [dirtyTables autorelease];
  358.     dirtyTables = nil;
  359. }
  360.  
  361. - (void)dropDirtyTables
  362. {
  363.     NSEnumerator *enumerator;
  364.     NSString *tableName;
  365.  
  366.     if (!dirtyTables)
  367.         return;
  368.  
  369.     enumerator = [dirtyTables objectEnumerator];
  370.  
  371.     while (tableName = [enumerator nextObject])
  372.         [[self infoForTable:tableName] removeObjectForKey:DataKey];
  373.  
  374.     [dirtyTables autorelease];
  375.     dirtyTables = nil;
  376. }
  377.  
  378. @end
  379.