home *** CD-ROM | disk | FTP | other *** search
/ OpenStep (Enterprise) / OpenStepENTCD.toast / OEDEV / EODEV.Z / FlatFileChannel.m < prev    next >
Encoding:
Text File  |  1996-08-23  |  16.7 KB  |  549 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 "FlatFileChannel.h"
  10. #import "FlatFileContext.h"
  11. #import "FlatFileAdaptor.h"
  12. #import "FlatFileColumn.h"
  13. #import "FileScanning.h"
  14.  
  15. @interface NSObject (FlatFileAdaptorFormatting)
  16.  
  17. - (NSString *)flatFileFormattedDescription;
  18.  
  19. @end
  20.  
  21. @interface NSCalendarDate (FlatFileAdaptorFormatting)
  22.  
  23. - (NSString *)flatFileFormattedDescription;
  24.  
  25. @end
  26.  
  27. @interface FlatFileChannel (FlatFileChannelPrivate)
  28.  
  29. - (NSDictionary *)dictionaryFromRow:(char *)row length:(int)length entity:(EOEntity *)entity;
  30. - (NSData *)dataFromDictionary:(NSDictionary *)dict entity:(EOEntity *)entity;
  31. - (void)selectFromEntity:(EOEntity *)entity qualifier:(EOQualifier *)qualifier recordIndices:(BOOL)record;
  32. - (void)cleanupFetch;
  33. - (void)buildColumnsForAttributes:(NSArray *)attributes;
  34. - (void)createTable:(NSString *)tableName withColumnList:(NSString *)columnList;
  35. - (void)dropTable:(NSString *)tableName;
  36.  
  37. @end
  38.  
  39. @implementation FlatFileChannel
  40.  
  41. - initWithFlatFileContext:(FlatFileContext *)context
  42. {
  43.     [super initWithAdaptorContext:context];
  44.  
  45.     rowSeparator = [[context rowSeparator] retain];
  46.     columnSeparator = [[context columnSeparator] retain];
  47.     
  48.     return self;
  49. }
  50.  
  51. - (void)dealloc
  52. {
  53.     [rowSeparator release];
  54.     [columnSeparator release];
  55.  
  56.     if (columns) {
  57.         [columns release];
  58.         [columnAttributes release];
  59.     }
  60.  
  61.     if (resultSet) {
  62.         [resultEnumerator release];
  63.         [resultSet release];
  64.     }
  65.  
  66.     [super dealloc];
  67. }
  68.  
  69. - (void)openChannel
  70. {
  71.     _flags.isOpen = YES;
  72. }
  73.  
  74. - (void)closeChannel
  75. {
  76.     if ([self isOpen]) {
  77.         if ([self isFetchInProgress])
  78.             [self cancelFetch];
  79.  
  80.         _flags.isOpen = NO;
  81.     }
  82. }
  83.  
  84. - (BOOL)isOpen
  85. {
  86.     return _flags.isOpen;
  87. }
  88.  
  89. - (void)insertRow:(NSDictionary *)row forEntity:(EOEntity *)entity
  90. {
  91.     NSMutableData *table;
  92.  
  93.     [(FlatFileContext *)[self adaptorContext] autoBeginTransaction];
  94.  
  95.     table = [(FlatFileContext *)_context dataForTable:[entity externalName]];
  96.     [table appendData:[self dataFromDictionary:row entity:entity]];
  97.     [table appendBytes:[rowSeparator cString] length:[rowSeparator cStringLength]];
  98.     [(FlatFileContext *)_context channel:self didDirtyTable:[entity externalName]];
  99.  
  100.     [(FlatFileContext *)[self adaptorContext] autoCommitTransaction];
  101.  
  102. }
  103.  
  104. - (unsigned)updateValues:(NSDictionary *)row inRowsDescribedByQualifier:(EOQualifier *)qualifier entity:(EOEntity *)entity
  105. {
  106.     NSMutableData *table;
  107.     NSMutableArray *matches = [NSMutableArray array];
  108.     NSDictionary *dict;
  109.     NSMutableDictionary *dictToUpdate;
  110.     NSEnumerator *enumerator;
  111.     char *begin, *currentRow, *nextRow, *end, *delimiter;
  112.     int length;
  113.     unsigned updates;
  114.  
  115.     [(FlatFileContext *)[self adaptorContext] autoBeginTransaction];
  116.  
  117.     [self setAttributesToFetch:[entity attributes]];
  118.     table = [(FlatFileContext *)_context dataForTable:[entity externalName]];
  119.     begin = (char *)[table mutableBytes];
  120.     currentRow = begin;
  121.     end = begin + [table length] - 1;
  122.     delimiter = (char *)[rowSeparator cString];
  123.  
  124.     while (currentRow) {
  125.         nextRow = FFNextTokenIn(currentRow, &length, end, delimiter);
  126.         dict = [self dictionaryFromRow:currentRow length:length entity:entity];
  127.         if (dict && (!qualifier || [(id <EOQualifierEvaluation>)qualifier evaluateWithObject:dict])) {
  128.             [matches addObject:[[[NSMutableDictionary alloc] initWithDictionary:dict] autorelease]];
  129.             if (nextRow) {
  130.                 memmove((void *)currentRow, (const void *)nextRow, 1 + end - nextRow);
  131.                 [table setLength:(1 + end - begin) - (nextRow - currentRow)];
  132.                 end -= (1 + nextRow - currentRow);
  133.                 nextRow = currentRow;
  134.             } else
  135.                 [table setLength:(1 + end - begin) - (1 + end - currentRow)];
  136.         }
  137.         currentRow = nextRow;
  138.     }
  139.  
  140.     enumerator = [matches objectEnumerator];
  141.     while (dictToUpdate = [enumerator nextObject]) {
  142.         [dictToUpdate addEntriesFromDictionary:row];
  143.         [self insertRow:dictToUpdate forEntity:entity];
  144.     }
  145.  
  146.     updates = [matches count];
  147.     if (updates)
  148.         [(FlatFileContext *)_context channel:self didDirtyTable:[entity externalName]];
  149.  
  150.     [(FlatFileContext *)[self adaptorContext] autoCommitTransaction];
  151.  
  152.     return updates;
  153. }
  154.  
  155. - (unsigned)deleteRowsDescribedByQualifier:(EOQualifier *)qualifier entity:(EOEntity *)entity
  156. {
  157.     NSMutableData *table;
  158.     NSDictionary *dict;
  159.     char *begin, *currentRow, *nextRow, *end, *delimiter;
  160.     int length;
  161.     unsigned deleted = 0;
  162.  
  163.     [(FlatFileContext *)[self adaptorContext] autoBeginTransaction];
  164.  
  165.     [self setAttributesToFetch:[entity attributes]];
  166.     table = [(FlatFileContext *)_context dataForTable:[entity externalName]];
  167.     begin = (char *)[table mutableBytes];
  168.     currentRow = begin;
  169.     end = begin + [table length] - 1;
  170.     delimiter = (char *)[rowSeparator cString];
  171.  
  172.     while (currentRow) {
  173.         nextRow = FFNextTokenIn(currentRow, &length, end, delimiter);
  174.         dict = [self dictionaryFromRow:currentRow length:length entity:entity];
  175.         if (dict && (!qualifier || [(id <EOQualifierEvaluation>)qualifier evaluateWithObject:dict])) {
  176.             if (nextRow) {
  177.                 memmove((void *)currentRow, (const void *)nextRow, 1 + end - nextRow);
  178.                 [table setLength:(1 + end - begin) - (nextRow - currentRow)];
  179.                 end -= (1 + nextRow - currentRow);
  180.                 nextRow = currentRow;
  181.                 deleted++;
  182.             } else
  183.                 [table setLength:(1 + end - begin) - (1 + end - currentRow)];
  184.         }
  185.         currentRow = nextRow;
  186.     }
  187.  
  188.     [(FlatFileContext *)_context channel:self didDirtyTable:[entity externalName]];
  189.  
  190.     [(FlatFileContext *)[self adaptorContext] autoCommitTransaction];
  191.  
  192.     return deleted;
  193. }
  194.  
  195. - (void)selectAttributes:(NSArray *)attributes fetchSpecification:(EOFetchSpecification *)spec lock:(BOOL)yn entity:(EOEntity *)entity
  196. {
  197.     NSData *table;
  198.     NSDictionary *dict;
  199.     NSArray *sortOrderings = [spec sortOrderings];
  200.     EOQualifier *qualifier = [spec qualifier];
  201.     char *currentRow, *nextRow, *end, *delimiter;
  202.     int length;
  203.  
  204.     [(FlatFileContext *)[self adaptorContext] autoBeginTransaction];
  205.  
  206.     if (_debug) NSLog(@"%s: fetching with spec: %@", sel_getName(_cmd), spec);
  207.     
  208.     if (_delegateRespondsTo.shouldSelectAttributes) {
  209.         attributes = [[NSMutableArray alloc] initWithArray:attributes];
  210.         if (![_delegate adaptorChannel:self shouldSelectAttributes:(NSMutableArray *)attributes fetchSpecification:spec lock:yn entity:entity])
  211.             return;
  212.     }
  213.  
  214.     resultSet = [[NSMutableArray array] retain];
  215.     [self setAttributesToFetch:attributes];
  216.  
  217.     table = [(FlatFileContext *)_context dataForTable:[entity externalName]];
  218.     currentRow = (char *)[table bytes];
  219.     end = currentRow + [table length] - 1;
  220.     delimiter = (char *)[rowSeparator cString];
  221.  
  222.     while (currentRow) {
  223.         nextRow = FFNextTokenIn(currentRow, &length, end, delimiter);
  224.         if (length) {
  225.             dict = [self dictionaryFromRow:currentRow length:length entity:entity];
  226.             if (dict && (!qualifier || [(id <EOQualifierEvaluation>)qualifier evaluateWithObject:dict])) {
  227.                 [resultSet addObject:dict];
  228.             }
  229.         }
  230.         currentRow = nextRow;
  231.     }
  232.  
  233.     if (_debug) NSLog(@"%s: match %d rows", sel_getName(_cmd), [resultSet count]);
  234.  
  235.     if ([resultSet count]) {
  236.         if (sortOrderings)
  237.             [resultSet sortUsingKeyOrderArray:sortOrderings];
  238.  
  239.         _flags.isFetchInProgress = YES;
  240.         resultEnumerator = [[resultSet objectEnumerator] retain];
  241.     } else
  242.         [self cleanupFetch];
  243.  
  244.     if (_delegateRespondsTo.didSelectAttributes)
  245.         [_delegate adaptorChannel:self didSelectAttributes:attributes fetchSpecification:spec lock:yn entity:entity];
  246. }
  247.  
  248. - (void)evaluateExpression:(EOSQLExpression *)expression
  249. {
  250.     NSScanner *scanner = [NSScanner scannerWithString:[expression statement]];
  251.     NSString *operation, *tableName, *columnList;
  252.     NSCharacterSet *whitespace, *alpha;
  253.     BOOL createTable = NO, isValidExpression = NO;
  254.  
  255.     whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet];
  256.     alpha = [NSCharacterSet alphanumericCharacterSet];
  257.  
  258.     [scanner scanUpToCharactersFromSet:alpha intoString:NULL];
  259.  
  260.     if ([scanner scanUpToCharactersFromSet:whitespace intoString:&operation]) {
  261.         createTable = [operation isEqualToString:@"create"];
  262.         if (createTable || [operation isEqualToString:@"drop"]) {
  263.             [scanner scanUpToCharactersFromSet:alpha intoString:NULL];
  264.             if ([scanner scanUpToCharactersFromSet:whitespace intoString:&tableName]) {
  265.                 if (createTable) {
  266.                     columnList = [expression listString];
  267.                     if ([columnList length]) {
  268.                         isValidExpression = YES;
  269.                         [self createTable:tableName withColumnList:columnList];
  270.                     }
  271.                 } else {
  272.                     isValidExpression = YES;
  273.                     [self dropTable:tableName];
  274.                 }
  275.             }
  276.         }
  277.     }
  278.  
  279.     if (!isValidExpression)
  280.         [NSException raise:EOGeneralAdaptorException format:@"can't evaluate %@", expression];
  281. }
  282.  
  283. - (void)createTable:(NSString *)tableName withColumnList:(NSString *)columnList
  284. {
  285.     NSString *fullPath;
  286.     NSArray *components;
  287.     NSData *headerData;
  288.     NSFileManager *fileManager = [NSFileManager defaultManager];
  289.  
  290.     fullPath = [[(FlatFileContext *)_context path] stringByAppendingPathComponent:tableName];
  291.     if ([fileManager fileExistsAtPath:fullPath])
  292.         [NSException raise:EOGeneralAdaptorException format:@"%@ already exists", fullPath];
  293.  
  294.     if ([columnList length]) {
  295.         components = [columnList componentsSeparatedByString:@", "];
  296.         columnList = [[components componentsJoinedByString:columnSeparator] stringByAppendingString:rowSeparator];
  297.     }
  298.     headerData = [NSData dataWithBytes:[columnList cString] length:[columnList cStringLength]];
  299.  
  300.     if (![headerData writeToFile:fullPath atomically:YES])
  301.         [NSException raise:EOGeneralAdaptorException format:@"can't create %@", fullPath];
  302. }
  303.  
  304. - (void)dropTable:(NSString *)tableName
  305. {
  306.     NSString *fullPath;
  307.     NSFileManager *fileManager = [NSFileManager defaultManager];
  308.  
  309.     fullPath = [[(FlatFileContext *)_context path] stringByAppendingPathComponent:tableName];
  310.  
  311.     if ([fileManager fileExistsAtPath:fullPath])
  312.         if (![fileManager removeFileAtPath:fullPath handler:nil])
  313.             [NSException raise:EOGeneralAdaptorException format:@"can't remove %@", fullPath];
  314. }
  315.  
  316. - (void)setAttributesToFetch:(NSArray *)attributes {
  317.     [selectedAttributes autorelease];
  318.     selectedAttributes = [attributes retain];
  319.     [self buildColumnsForAttributes:attributes];
  320. }
  321.  
  322. - (NSArray *)attributesToFetch
  323. {
  324.     return selectedAttributes;
  325. }
  326.  
  327. - (BOOL)isFetchInProgress
  328. {
  329.     return _flags.isFetchInProgress;
  330. }
  331.  
  332. - (NSArray *)describeResults
  333. {
  334.     if (![self isFetchInProgress])
  335.         [NSException raise:NSInternalInconsistencyException format:@"%@ -- %@ 0x%x: attempt to describe results with no fetch in progress", NSStringFromSelector(_cmd), NSStringFromClass([self class]), self];
  336.  
  337.     return [self attributesToFetch];
  338. }
  339.  
  340. - (NSMutableDictionary *)fetchRowWithZone:(NSZone *)zone
  341. {
  342.     NSMutableDictionary *result;
  343.  
  344.     if (_delegateRespondsTo.willFetchRow) {
  345.         [_delegate adaptorChannelWillFetchRow:self];
  346.     }
  347.  
  348.     if (!_flags.isFetchInProgress)
  349.         return nil;
  350.  
  351.     result = [resultEnumerator nextObject];
  352.     if (result) {
  353.         if (_delegateRespondsTo.didFetchRow)
  354.             [_delegate adaptorChannel:self didFetchRow:result];
  355.     }
  356.     else
  357.         [self cleanupFetch];
  358.  
  359.     return result;
  360. }
  361.  
  362. - (void)cancelFetch
  363. {
  364.     [self cleanupFetch];
  365. }
  366.  
  367. - (NSDictionary *)primaryKeyForNewRowWithEntity:(EOEntity *)entity
  368. {
  369.     return nil;
  370. }
  371.  
  372. - (void)cleanupFetch
  373. {
  374.     _flags.isFetchInProgress = NO;
  375.     [resultEnumerator autorelease];
  376.     [resultSet autorelease];
  377.     resultEnumerator = nil;
  378.     resultSet = nil;
  379.     [(FlatFileContext *)[self adaptorContext] autoCommitTransaction];
  380. }
  381.  
  382. - (NSDictionary *)dictionaryFromRow:(char *)row length:(int)rowLength entity:(EOEntity *)entity
  383. {
  384.     FlatFileColumn *column;
  385.     NSDictionary *dict;
  386.     NSZone *zone = [self zone];
  387.     EONull *nullValue = [EONull null];
  388.     int i, columnCount, columnLength;
  389.     id valueBuffer [100], *values;
  390.     char *current, *next, *end, *delimiter;
  391.  
  392.     if (!rowLength)
  393.         return nil;
  394.  
  395.     columnCount = [columns count];
  396.  
  397.     if (columnCount > 100)
  398.         values = (id *)malloc(columnCount * sizeof(id));
  399.     else
  400.         values = valueBuffer;
  401.  
  402.     current = row;
  403.     delimiter = (char *)[columnSeparator cString];
  404.     end = current + rowLength - 1;
  405.     next = NULL;
  406.     
  407.     for (i = 0; i < columnCount; i++) {
  408.         column = [columns objectAtIndex:i];
  409.         if (current)
  410.             next = FFNextTokenIn(current, &columnLength, end, delimiter);
  411.  
  412.         if (current && columnLength)
  413.             values[[column bindingIndex]] = [column newValueForBytes:current length:columnLength];
  414.         else
  415.             values[[column bindingIndex]] = nullValue;
  416.  
  417.         current = next;
  418.     }
  419.     dict = [self dictionaryWithObjects:values forAttributes:columnAttributes zone:zone];
  420.  
  421.     if (values != valueBuffer)
  422.         free(values);
  423.  
  424.     return dict;
  425. }
  426.  
  427. - (NSData *)dataFromDictionary:(NSDictionary *)dict entity:(EOEntity *)entity
  428. {
  429.     NSArray *attributes = [entity attributes];
  430.     NSString *string, *columnName;
  431.     NSDictionary *columnIndexMap;
  432.     NSMutableData *row = [NSMutableData data];
  433.     NSNumber *columnIndex;
  434.     EOAttribute *attribute;
  435.     EONull *nullValue = [EONull null];
  436.     id *strings, stringBuffer[100], value;
  437.     int i, count = [attributes count], delimiterLength, columnCount = 0;
  438.     char *delimiter;
  439.     
  440.     columnIndexMap = [(FlatFileContext *)_context columnIndexMapForTable:[entity externalName]];
  441.     delimiter = (char *)[columnSeparator cString];
  442.     delimiterLength = [columnSeparator cStringLength];
  443.     strings = (count > 100) ? (id *)malloc(count * sizeof(id)) : stringBuffer;
  444.  
  445.     for (i = 0; i < count; i++) {
  446.         attribute = [attributes objectAtIndex:i];
  447.         columnName = [attribute columnName];
  448.         if (columnName) {
  449.             columnIndex = [columnIndexMap objectForKey:columnName];
  450.             value = [dict objectForKey:[attribute name]];
  451.  
  452.             if (value && (value != nullValue)) {
  453.                 value = [attribute adaptorValueByConvertingAttributeValue:value];
  454.                 strings[[columnIndex intValue]] = [value flatFileFormattedDescription];
  455.             } else
  456.                 strings[[columnIndex intValue]] = nil;
  457.  
  458.             columnCount++;
  459.         }
  460.     }
  461.  
  462.     for (i = 0; i < columnCount; i++) {
  463.         if (i)
  464.             [row appendBytes:delimiter length:delimiterLength];
  465.  
  466.         string = strings [i];
  467.         if (string)
  468.             [row appendBytes:[string cString] length:[string cStringLength]];
  469.     }
  470.  
  471.     if (strings != stringBuffer)
  472.         free(strings);
  473.  
  474.     return row;
  475. }
  476.  
  477.  
  478. - (void)buildColumnsForAttributes:(NSArray *)attributes
  479. {
  480.     NSString *tableName;
  481.     EOEntity *entity = [[attributes objectAtIndex:0] entity];
  482.     NSEnumerator *enumerator;
  483.     EOAttribute *attribute;
  484.     NSString *columnName;
  485.     NSDictionary *columnIndexMap;
  486.     FlatFileColumn *column;
  487.     NSArray *allAttributes = [entity attributes];
  488.     int bindingIndex = 0, count = [allAttributes count];
  489.  
  490.     if (columns) {
  491.         [columns release];
  492.         [columnAttributes release];
  493.     }
  494.  
  495.     if (count) {
  496.         columnAttributes = [[NSMutableArray alloc] initWithCapacity:count];
  497.         columns = [[NSMutableArray alloc] initWithCapacity:count];
  498.         enumerator = [allAttributes objectEnumerator];
  499.         tableName = [entity externalName];
  500.         columnIndexMap = [(FlatFileContext *)_context columnIndexMapForTable:tableName];
  501.  
  502.         while (attribute = [enumerator nextObject]) {
  503.             columnName = [attribute columnName];
  504.             if (columnName) {
  505.                 column = [FlatFileColumn columnForAttribute:attribute];
  506.                 [columnAttributes addObject:attribute];
  507.                 [columns addObject:column];
  508.                 [column bindAtIndex:bindingIndex++];
  509.             }
  510.         }
  511.     }
  512.  
  513.     if (!bindingIndex) {
  514.         [columnAttributes release];
  515.         [columns release];
  516.         columnAttributes = nil;
  517.         columns = nil;
  518.     }
  519.  
  520.     selectedEntity = entity;
  521. }
  522.  
  523. @end
  524.  
  525. @implementation NSObject (FlatFileAdaptorFormatting)
  526.  
  527. - (NSString *)flatFileFormattedDescription
  528. {
  529.     return [self description];
  530. }
  531.  
  532. @end
  533.  
  534. @implementation NSCalendarDate (FlatFileAdaptorFormatting)
  535.  
  536. - (NSString *)flatFileFormattedDescription
  537. {
  538.     return [self descriptionWithCalendarFormat:FlatFileCalendarFormat];
  539. }
  540.  
  541. @end
  542.  
  543.  
  544.  
  545.  
  546.  
  547.  
  548.  
  549.