From 27eba105aef0a612e5a91af02a0b87b99b06ab78 Mon Sep 17 00:00:00 2001 From: stuconnolly Date: Tue, 6 Oct 2009 00:44:48 +0000 Subject: More export redesign work. CSV data generation is currently working using the old non-streaming code, but does not yet write to any files. Please note that this is still very much a work in progress. --- Source/SPCSVExporter.h | 7 +- Source/SPCSVExporter.m | 483 +++++++++++++++++++++----------------------- Source/SPExportController.h | 3 +- Source/SPExportController.m | 48 +++-- Source/SPExporter.h | 4 +- Source/SPExporter.m | 3 +- Source/SPExporterAccess.h | 40 ---- Source/SPSQLExporter.h | 3 - Source/SPSQLExporter.m | 18 ++ Source/SPXMLExporter.h | 32 +++ Source/SPXMLExporter.m | 48 +++++ Source/TableDocument.h | 2 +- Source/TableDocument.m | 21 +- 13 files changed, 374 insertions(+), 338 deletions(-) delete mode 100644 Source/SPExporterAccess.h create mode 100644 Source/SPXMLExporter.h create mode 100644 Source/SPXMLExporter.m (limited to 'Source') diff --git a/Source/SPCSVExporter.h b/Source/SPCSVExporter.h index 51569838..95f30b46 100644 --- a/Source/SPCSVExporter.h +++ b/Source/SPCSVExporter.h @@ -29,10 +29,7 @@ #import "SPExporter.h" @interface SPCSVExporter : SPExporter -{ - // CSV file - NSFileHandle *csvFileHandle; - +{ // CSV data NSArray *csvDataArray; MCPResult *csvDataResult; @@ -47,8 +44,6 @@ NSArray *csvTableColumnNumericStatus; } -@property (readwrite, retain) NSFileHandle *csvFileHandle; - @property (readwrite, retain) NSArray *csvDataArray; @property (readwrite, retain) MCPResult *csvDataResult; diff --git a/Source/SPCSVExporter.m b/Source/SPCSVExporter.m index b508172b..82f43a79 100644 --- a/Source/SPCSVExporter.m +++ b/Source/SPCSVExporter.m @@ -26,16 +26,8 @@ #import "SPCSVExporter.h" #import "SPArrayAdditions.h" -@interface SPCSVExporter (PrivateAPI) - -- (void)_startCSVExportInBackgroundThread; - -@end - @implementation SPCSVExporter -@synthesize csvFileHandle; - @synthesize csvDataArray; @synthesize csvDataResult; @@ -48,292 +40,273 @@ @synthesize csvTableColumnNumericStatus; /** - * Start the CSV export process. + * Start the CSV data conversion process. This method is automatically called when an instance of this object + * is placed on an NSOperationQueue. Do not call it directly as there is no manual multithreading. */ -- (BOOL)startExportProcess -{ - // Check that we have all the required info before starting the export - if ((![self csvFileHandle]) || - (![self csvOutputFieldNames]) || - (![self csvFieldSeparatorString]) || - (![self csvEscapeString]) || - (![self csvLineEndingString]) || - (![self csvTableColumnNumericStatus])) - { - return NO; - } - - // Check that the CSV output options are not just empty strings or empty arrays - if ((![[self csvFieldSeparatorString] isEqualToString:@""]) || - (![[self csvEscapeString] isEqualToString:@""]) || - (![[self csvLineEndingString] isEqualToString:@""]) || - ([[self csvTableColumnNumericStatus] count] != 0)) - { - return NO; - } - - // Check that we have at least some data to export - if ((![self csvDataArray]) && (![self csvDataResult])) return NO; - - // Tell the delegate that we are starting the export process - if (delegate && [delegate respondsToSelector:@selector(exportProcessDidStart:)]) { - [delegate exportProcessDidStart:self]; - } - - [self setExportProcessIsRunning:YES]; +- (void)main +{ + @try { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - // Start the export in a new thread - [NSThread detachNewThreadSelector:@selector(_startCSVExportInBackgroundThread) toTarget:self withObject:nil]; - - [self setExportProcessIsRunning:NO]; - - // Tell the delegate that the export process has ended - if (delegate && [delegate respondsToSelector:@selector(exportProcessDidEnd:)]) { - [delegate exportProcessDidEnd:self]; - } - - return YES; -} - -/** - * Stop the CSV export process by killing the export thread and cleaning up if its running. - */ -- (BOOL)stopExportProcess -{ - if (![self exportProcessIsRunning]) return NO; - - // Kill the running thread here - - return YES; -} - -/** - * Dealloc - */ -- (void)dealloc -{ - [csvFileHandle release], csvFileHandle = nil; - [csvDataArray release], csvDataArray = nil; - [csvDataResult release], csvDataResult = nil; - [csvFieldSeparatorString release], csvFieldSeparatorString = nil; - [csvEnclosingCharacterString release], csvEnclosingCharacterString = nil; - [csvEscapeString release], csvEscapeString = nil; - [csvLineEndingString release], csvLineEndingString = nil; - [csvNULLString release], csvNULLString = nil; - [csvTableColumnNumericStatus release], csvTableColumnNumericStatus = nil; - - [super dealloc]; -} - -@end - -@implementation SPCSVExporter (PrivateAPI) - -/** - * Starts the export process in a background thread. - */ -- (void)_startCSVExportInBackgroundThread -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSMutableArray *csvRow = [NSMutableArray array]; - NSMutableString *csvCell = [NSMutableString string]; - NSMutableString *csvString = [NSMutableString string]; - - NSScanner *csvNumericTester; - NSString *escapedEscapeString, *escapedFieldSeparatorString, *escapedEnclosingString, *escapedLineEndString, *dataConversionString; - - BOOL csvCellIsNumeric; - BOOL quoteFieldSeparators = [[self csvEnclosingCharacterString] isEqualToString:@""]; - - NSUInteger i, j, startingRow, totalRows; - - if ([self csvDataResult] != nil && [[self csvDataResult] numOfRows]) [[self csvDataResult] dataSeek:0]; - - // Detect and restore special characters being used as terminating or line end strings - NSMutableString *tempSeparatorString = [NSMutableString stringWithString:[self csvFieldSeparatorString]]; + NSMutableArray *csvRow = [NSMutableArray array]; + NSMutableString *csvCell = [NSMutableString string]; + NSMutableString *csvString = [NSMutableString string]; + NSMutableString *csvData = [NSMutableString string]; - // Escape tabs, line endings and carriage returns - [tempSeparatorString replaceOccurrencesOfString:@"\\t" withString:@"\t" - options:NSLiteralSearch - range:NSMakeRange(0, [tempSeparatorString length])]; - - [tempSeparatorString replaceOccurrencesOfString:@"\\n" withString:@"\n" - options:NSLiteralSearch - range:NSMakeRange(0, [tempSeparatorString length])]; - - [tempSeparatorString replaceOccurrencesOfString:@"\\r" withString:@"\r" - options:NSLiteralSearch - range:NSMakeRange(0, [tempSeparatorString length])]; - - // Set the new field separator string - [self setCsvFieldSeparatorString:[NSString stringWithString:tempSeparatorString]]; - - NSMutableString *tempLineEndString = [NSMutableString stringWithString:[self csvLineEndingString]]; + NSScanner *csvNumericTester; + NSString *escapedEscapeString, *escapedFieldSeparatorString, *escapedEnclosingString, *escapedLineEndString, *dataConversionString; - // Escape tabs, line endings and carriage returns - [tempLineEndString replaceOccurrencesOfString:@"\\t" withString:@"\t" - options:NSLiteralSearch - range:NSMakeRange(0, [tempLineEndString length])]; - - - [tempLineEndString replaceOccurrencesOfString:@"\\n" withString:@"\n" - options:NSLiteralSearch - range:NSMakeRange(0, [tempLineEndString length])]; - - [tempLineEndString replaceOccurrencesOfString:@"\\r" withString:@"\r" - options:NSLiteralSearch - range:NSMakeRange(0, [tempLineEndString length])]; - - // Set the new line ending string - [self setCsvLineEndingString:[NSString stringWithString:tempLineEndString]]; - - // Set up escaped versions of strings for substitution within the loop - escapedEscapeString = [[self csvEscapeString] stringByAppendingString:[self csvEscapeString]]; - escapedFieldSeparatorString = [[self csvEscapeString] stringByAppendingString:[self csvFieldSeparatorString]]; - escapedEnclosingString = [[self csvEscapeString] stringByAppendingString:[self csvEnclosingCharacterString]]; - escapedLineEndString = [[self csvEscapeString] stringByAppendingString:[self csvLineEndingString]]; - - // Determine the total number of rows and starting row depending on supplied data format - if ([self csvDataArray] == nil) { - startingRow = [self csvOutputFieldNames] ? -1 : 0; - totalRows = [[self csvDataResult] numOfRows]; - } - else { - startingRow = [self csvOutputFieldNames] ? 0 : 1; - totalRows = [[self csvDataArray] count]; - } - - // Walk through the supplied data constructing the CSV string - for (i = startingRow; i < totalRows; i++) - { - // Check if we should stop and exit the export operation - if ([self exportProcessShouldExit]) { - [pool release]; - + BOOL csvCellIsNumeric; + BOOL quoteFieldSeparators = [[self csvEnclosingCharacterString] isEqualToString:@""]; + + NSUInteger i, j, startingRow, totalRows; + + // Check that we have all the required info before starting the export + if ((![self csvOutputFieldNames]) || + (![self csvFieldSeparatorString]) || + (![self csvEscapeString]) || + (![self csvLineEndingString]) || + (![self csvTableColumnNumericStatus])) + { return; } + + // Check that the CSV output options are not just empty strings or empty arrays + if (([[self csvFieldSeparatorString] isEqualToString:@""]) || + ([[self csvEscapeString] isEqualToString:@""]) || + ([[self csvLineEndingString] isEqualToString:@""]) || + ([[self csvTableColumnNumericStatus] count] == 0)) + { + return; + } + + // Check that we have at least some data to export + if ((![self csvDataArray]) && (![self csvDataResult])) return; + + // Tell the delegate that we are starting the export process + if (delegate && [delegate respondsToSelector:@selector(exportProcessDidStart:)]) { + [delegate exportProcessDidStart:self]; + } - // Update the progress value - if (totalRows) [self setExportProgressValue:(((i + 1) * 100) / totalRows)]; + // Mark the process as running + [self setExportProcessIsRunning:YES]; - // Retrieve the row from the supplied data + if ([self csvDataResult] != nil && [[self csvDataResult] numOfRows]) [[self csvDataResult] dataSeek:0]; + + // Detect and restore special characters being used as terminating or line end strings + NSMutableString *tempSeparatorString = [NSMutableString stringWithString:[self csvFieldSeparatorString]]; + + // Escape tabs, line endings and carriage returns + [tempSeparatorString replaceOccurrencesOfString:@"\\t" withString:@"\t" + options:NSLiteralSearch + range:NSMakeRange(0, [tempSeparatorString length])]; + + [tempSeparatorString replaceOccurrencesOfString:@"\\n" withString:@"\n" + options:NSLiteralSearch + range:NSMakeRange(0, [tempSeparatorString length])]; + + [tempSeparatorString replaceOccurrencesOfString:@"\\r" withString:@"\r" + options:NSLiteralSearch + range:NSMakeRange(0, [tempSeparatorString length])]; + + // Set the new field separator string + [self setCsvFieldSeparatorString:[NSString stringWithString:tempSeparatorString]]; + + NSMutableString *tempLineEndString = [NSMutableString stringWithString:[self csvLineEndingString]]; + + // Escape tabs, line endings and carriage returns + [tempLineEndString replaceOccurrencesOfString:@"\\t" withString:@"\t" + options:NSLiteralSearch + range:NSMakeRange(0, [tempLineEndString length])]; + + + [tempLineEndString replaceOccurrencesOfString:@"\\n" withString:@"\n" + options:NSLiteralSearch + range:NSMakeRange(0, [tempLineEndString length])]; + + [tempLineEndString replaceOccurrencesOfString:@"\\r" withString:@"\r" + options:NSLiteralSearch + range:NSMakeRange(0, [tempLineEndString length])]; + + // Set the new line ending string + [self setCsvLineEndingString:[NSString stringWithString:tempLineEndString]]; + + // Set up escaped versions of strings for substitution within the loop + escapedEscapeString = [[self csvEscapeString] stringByAppendingString:[self csvEscapeString]]; + escapedFieldSeparatorString = [[self csvEscapeString] stringByAppendingString:[self csvFieldSeparatorString]]; + escapedEnclosingString = [[self csvEscapeString] stringByAppendingString:[self csvEnclosingCharacterString]]; + escapedLineEndString = [[self csvEscapeString] stringByAppendingString:[self csvLineEndingString]]; + + // Determine the total number of rows and starting row depending on supplied data format if ([self csvDataArray] == nil) { - // Header row - [csvRow setArray:(i == -1) ? [[self csvDataResult] fetchFieldNames] : [[self csvDataResult] fetchRowAsArray]]; + startingRow = [self csvOutputFieldNames] ? 1 : 0; + totalRows = [[self csvDataResult] numOfRows]; } else { - [csvRow setArray:NSArrayObjectAtIndex([self csvDataArray], i)]; + startingRow = [self csvOutputFieldNames] ? 0 : 1; + totalRows = [[self csvDataArray] count]; } - [csvString setString:@""]; - - for (j = 0; j < [csvRow count]; j++) - { - // For NULL objects supplied from a queryResult, add an unenclosed null string as per prefs - if ([[csvRow objectAtIndex:j] isKindOfClass:[NSNull class]]) { - [csvString appendString:[self csvNULLString]]; - - if (j < [csvRow count] - 1) [csvString appendString:[self csvFieldSeparatorString]]; + [csvData setString:@""]; - continue; + // Walk through the supplied data constructing the CSV string + for (i = startingRow; i < totalRows; i++) + { + // Check to see if the operation has been cancelled. If so exit the loop. + if ([self isCancelled]) { + break; } - // Retrieve the contents of this cell - if ([NSArrayObjectAtIndex(csvRow, j) isKindOfClass:[NSData class]]) { - dataConversionString = [[NSString alloc] initWithData:NSArrayObjectAtIndex(csvRow, j) encoding:[self exportOutputEncoding]]; - - if (dataConversionString == nil) { - dataConversionString = [[NSString alloc] initWithData:NSArrayObjectAtIndex(csvRow, j) encoding:NSASCIIStringEncoding]; - } - - [csvCell setString:[NSString stringWithString:dataConversionString]]; - [dataConversionString release]; + // Update the progress value + if (totalRows) [self setExportProgressValue:(((i + 1) * 100) / totalRows)]; + + // Retrieve the row from the supplied data + if ([self csvDataArray] == nil) { + // Header row + [csvRow setArray:(i == -1) ? [[self csvDataResult] fetchFieldNames] : [[self csvDataResult] fetchRowAsArray]]; } else { - [csvCell setString:[NSArrayObjectAtIndex(csvRow, j) description]]; + [csvRow setArray:NSArrayObjectAtIndex([self csvDataArray], i)]; } - // For NULL values supplied via an array add the unenclosed null string as set in preferences - if ([csvCell isEqualToString:[self csvNULLString]]) { - [csvString appendString:[self csvNULLString]]; - } - // Add empty strings as a pair of enclosing characters. - else if ([csvCell length] == 0) { - [csvString appendString:[self csvEnclosingCharacterString]]; - [csvString appendString:[self csvEnclosingCharacterString]]; + [csvString setString:@""]; + + for (j = 0; j < [csvRow count]; j++) + { + // For NULL objects supplied from a queryResult, add an unenclosed null string as per prefs + if ([[csvRow objectAtIndex:j] isKindOfClass:[NSNull class]]) { + [csvString appendString:[self csvNULLString]]; + + if (j < [csvRow count] - 1) [csvString appendString:[self csvFieldSeparatorString]]; + + continue; + } - } - else { - // Test whether this cell contains a number + // Retrieve the contents of this cell if ([NSArrayObjectAtIndex(csvRow, j) isKindOfClass:[NSData class]]) { - csvCellIsNumeric = NO; + dataConversionString = [[NSString alloc] initWithData:NSArrayObjectAtIndex(csvRow, j) encoding:[self exportOutputEncoding]]; + + if (dataConversionString == nil) { + dataConversionString = [[NSString alloc] initWithData:NSArrayObjectAtIndex(csvRow, j) encoding:NSASCIIStringEncoding]; + } + + [csvCell setString:[NSString stringWithString:dataConversionString]]; + [dataConversionString release]; } - // If an array of bools supplying information as to whether the column is numeric has been supplied, use it. - else if ([self csvTableColumnNumericStatus] != nil) { - csvCellIsNumeric = [NSArrayObjectAtIndex([self csvTableColumnNumericStatus], j) boolValue]; - } - // Or fall back to testing numeric content via an NSScanner. else { - csvNumericTester = [NSScanner scannerWithString:csvCell]; - csvCellIsNumeric = [csvNumericTester scanFloat:nil] && - [csvNumericTester isAtEnd] && - ([csvCell characterAtIndex:0] != '0' || - [csvCell length] == 1 || - ([csvCell length] > 1 && - [csvCell characterAtIndex:1] == '.')); + [csvCell setString:[NSArrayObjectAtIndex(csvRow, j) description]]; } - // Escape any occurrences of the escaping character - [csvCell replaceOccurrencesOfString:[self csvEscapeString] - withString:escapedEscapeString - options:NSLiteralSearch - range:NSMakeRange(0, [csvCell length])]; - - // Escape any occurrences of the enclosure string - if (![[self csvEscapeString] isEqualToString:[self csvEnclosingCharacterString]]) { - [csvCell replaceOccurrencesOfString:[self csvEnclosingCharacterString] - withString:escapedEnclosingString + // For NULL values supplied via an array add the unenclosed null string as set in preferences + if ([csvCell isEqualToString:[self csvNULLString]]) { + [csvString appendString:[self csvNULLString]]; + } + // Add empty strings as a pair of enclosing characters. + else if ([csvCell length] == 0) { + [csvString appendString:[self csvEnclosingCharacterString]]; + [csvString appendString:[self csvEnclosingCharacterString]]; + + } + else { + // Test whether this cell contains a number + if ([NSArrayObjectAtIndex(csvRow, j) isKindOfClass:[NSData class]]) { + csvCellIsNumeric = NO; + } + // If an array of bools supplying information as to whether the column is numeric has been supplied, use it. + else if ([self csvTableColumnNumericStatus] != nil) { + csvCellIsNumeric = [NSArrayObjectAtIndex([self csvTableColumnNumericStatus], j) boolValue]; + } + // Or fall back to testing numeric content via an NSScanner. + else { + csvNumericTester = [NSScanner scannerWithString:csvCell]; + csvCellIsNumeric = [csvNumericTester scanFloat:nil] && + [csvNumericTester isAtEnd] && + ([csvCell characterAtIndex:0] != '0' || + [csvCell length] == 1 || + ([csvCell length] > 1 && + [csvCell characterAtIndex:1] == '.')); + } + + // Escape any occurrences of the escaping character + [csvCell replaceOccurrencesOfString:[self csvEscapeString] + withString:escapedEscapeString options:NSLiteralSearch range:NSMakeRange(0, [csvCell length])]; - } - - // Escape occurrences of the line end character - [csvCell replaceOccurrencesOfString:[self csvLineEndingString] - withString:escapedLineEndString - options:NSLiteralSearch - range:NSMakeRange(0, [csvCell length])]; - - // If the string isn't quoted or otherwise enclosed, escape occurrences of the field separators - if (quoteFieldSeparators || csvCellIsNumeric) { - [csvCell replaceOccurrencesOfString:[self csvFieldSeparatorString] - withString:escapedFieldSeparatorString + + // Escape any occurrences of the enclosure string + if (![[self csvEscapeString] isEqualToString:[self csvEnclosingCharacterString]]) { + [csvCell replaceOccurrencesOfString:[self csvEnclosingCharacterString] + withString:escapedEnclosingString + options:NSLiteralSearch + range:NSMakeRange(0, [csvCell length])]; + } + + // Escape occurrences of the line end character + [csvCell replaceOccurrencesOfString:[self csvLineEndingString] + withString:escapedLineEndString options:NSLiteralSearch range:NSMakeRange(0, [csvCell length])]; + + // If the string isn't quoted or otherwise enclosed, escape occurrences of the field separators + if (quoteFieldSeparators || csvCellIsNumeric) { + [csvCell replaceOccurrencesOfString:[self csvFieldSeparatorString] + withString:escapedFieldSeparatorString + options:NSLiteralSearch + range:NSMakeRange(0, [csvCell length])]; + } + + // Write out the cell data by appending strings - this is significantly faster than stringWithFormat. + if (csvCellIsNumeric) { + [csvString appendString:csvCell]; + } + else { + [csvString appendString:[self csvEnclosingCharacterString]]; + [csvString appendString:csvCell]; + [csvString appendString:[self csvEnclosingCharacterString]]; + } } - // Write out the cell data by appending strings - this is significantly faster than stringWithFormat. - if (csvCellIsNumeric) { - [csvString appendString:csvCell]; - } - else { - [csvString appendString:[self csvEnclosingCharacterString]]; - [csvString appendString:csvCell]; - [csvString appendString:[self csvEnclosingCharacterString]]; - } + if (j < ([csvRow count] - 1)) [csvString appendString:[self csvFieldSeparatorString]]; } - if (j < ([csvRow count] - 1)) [csvString appendString:[self csvFieldSeparatorString]]; + // Append the line ending to the string for this row + [csvString appendString:[self csvLineEndingString]]; + [csvData appendString:csvString]; + } + + // Mark the process as not running + [self setExportProcessIsRunning:NO]; + + // Tell the delegate that the export process has ended + if (delegate && [delegate respondsToSelector:@selector(exportProcessDidEnd:)]) { + [delegate exportProcessDidEnd:self]; } - // Append the line ending to the string for this row - [csvString appendString:[self csvLineEndingString]]; + // Pass the resulting CSV data back to the delegate by calling the specified didEndSelector + [[self delegate] performSelectorOnMainThread:[self didEndSelector] withObject:csvData waitUntilDone:YES]; - // Write it to the fileHandle - [csvFileHandle writeData:[csvString dataUsingEncoding:[self exportOutputEncoding]]]; + [pool release]; } + @catch(NSException *e) { + + } +} + +/** + * Dealloc + */ +- (void)dealloc +{ + [csvDataArray release], csvDataArray = nil; + [csvDataResult release], csvDataResult = nil; + [csvFieldSeparatorString release], csvFieldSeparatorString = nil; + [csvEnclosingCharacterString release], csvEnclosingCharacterString = nil; + [csvEscapeString release], csvEscapeString = nil; + [csvLineEndingString release], csvLineEndingString = nil; + [csvNULLString release], csvNULLString = nil; + [csvTableColumnNumericStatus release], csvTableColumnNumericStatus = nil; - [pool release]; + [super dealloc]; } @end diff --git a/Source/SPExportController.h b/Source/SPExportController.h index 8653c345..ec396db5 100644 --- a/Source/SPExportController.h +++ b/Source/SPExportController.h @@ -45,7 +45,7 @@ enum { typedef NSUInteger SPExportSource; @interface SPExportController : NSObject -{ +{ // Table document IBOutlet id tableDocumentInstance; IBOutlet id tableWindow; @@ -107,6 +107,7 @@ typedef NSUInteger SPExportSource; BOOL exportCancelled; NSMutableArray *tables; MCPConnection *connection; + NSOperationQueue *operationQueue; } @property (readwrite, assign) BOOL exportCancelled; diff --git a/Source/SPExportController.m b/Source/SPExportController.m index ba6fab79..91a3a0b7 100644 --- a/Source/SPExportController.m +++ b/Source/SPExportController.m @@ -35,7 +35,7 @@ - (void)_loadTables; - (NSString *)_htmlEscapeString:(NSString *)string; - (void)_initializeExportUsingSelectedOptions; -- (BOOL)_exportTablesAsCSV:(NSArray *)exportTables toFileHandle:(NSFileHandle *)fileHandle usingDataExporter:(SPExporter *)exporter; +- (BOOL)_exportTablesAsCSV:(NSArray *)exportTables usingDataExporter:(SPExporter *)exporter; @end @@ -51,7 +51,9 @@ { if ((self = [super init])) { [self setExportCancelled:NO]; + tables = [[NSMutableArray alloc] init]; + operationQueue = [[NSOperationQueue alloc] init]; } return self; @@ -224,6 +226,7 @@ - (void)dealloc { [tables release], tables = nil; + [operationQueue release], operationQueue = nil; [super dealloc]; } @@ -232,6 +235,14 @@ @implementation SPExportController (PrivateAPI) +/** + * This method is called by SPCSVExporter objects once they have completed converting their data to CSV data. + */ +- (void)_csvDataAvialble:(NSString *)data +{ + +} + /** * Loads all the available database tables in table view. */ @@ -335,9 +346,7 @@ break; case SP_CSV_EXPORT: csvExporter = [[SPCSVExporter alloc] init]; - - [csvExporter setCsvFileHandle:[NSFileHandle fileHandleForWritingAtPath:@"/Users/stuart/Desktop/output.csv"]]; - + [csvExporter setCsvOutputFieldNames:[exportCSVIncludeFieldNamesCheck state]]; [csvExporter setCsvFieldSeparatorString:[exportCSVFieldsTerminatedField stringValue]]; [csvExporter setCsvEnclosingCharacterString:[exportCSVFieldsWrappedField stringValue]]; @@ -363,8 +372,9 @@ break; } - // Set the exporter's delegate + // Set the exporter's delegate and didEndSelector [exporter setDelegate:self]; + [exporter setDidEndSelector:@selector(_csvDataAvialble:)]; switch (exportSource) { @@ -375,7 +385,7 @@ break; case SP_TABLE_EXPORT: - [self _exportTablesAsCSV:exportTables toFileHandle:[NSFileHandle fileHandleForWritingAtPath:@"/Users/stuart/Desktop/output.csv"] usingDataExporter:exporter]; + [self _exportTablesAsCSV:exportTables usingDataExporter:exporter]; break; } } @@ -384,7 +394,7 @@ * Exports the contents' of the supplied array of tables using the supplied exporter and export type. Note that * this method currently only supports exporting in CSV and XML formats. */ -- (BOOL)_exportTablesAsCSV:(NSArray *)exportTables toFileHandle:(NSFileHandle *)fileHandle usingDataExporter:(SPExporter *)exporter +- (BOOL)_exportTablesAsCSV:(NSArray *)exportTables usingDataExporter:(SPCSVExporter *)exporter { NSUInteger tableCount, i, j; @@ -428,9 +438,7 @@ [infoString setString:[NSString stringWithFormat:@"Host: %@ Database: %@ Generation Time: %@%@%@", [tableDocumentInstance host], [tableDocumentInstance database], [NSDate date], csvLineEnd, csvLineEnd]]; } - - [fileHandle writeData:[infoString dataUsingEncoding:encoding]]; - + tableCount = [exportTables count]; // Loop through the tables @@ -447,9 +455,9 @@ [exportProgressIndicator startAnimation:self]; // For CSV exports of more than one table, output the name of the table - if (tableCount > 1) { + /*if (tableCount > 1) { [fileHandle writeData:[[NSString stringWithFormat:@"Table %@%@%@", tableName, csvLineEnd, csvLineEnd] dataUsingEncoding:encoding]]; - } + }*/ // Determine whether this table is a table or a view via the create table command, and get the table details MCPResult *queryResult = [connection queryString:[NSString stringWithFormat:@"SHOW CREATE TABLE %@", [tableName backtickQuotedString]]]; @@ -478,14 +486,19 @@ } } + [exporter setCsvTableColumnNumericStatus:tableColumnNumericStatus]; + // Retrieve all the content within this table queryResult = [connection queryString:[NSString stringWithFormat:@"SELECT * FROM %@", [tableName backtickQuotedString]]]; - + // Note any errors during retrieval if (![[connection getLastErrorMessage] isEqualToString:@""]) { [errors appendString:[NSString stringWithFormat:@"%@\n", [connection getLastErrorMessage]]]; } + // Assign the data to the exporter + [exporter setCsvDataResult:queryResult]; + // Update the progress text and set the progress bar back to determinate [exportProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Table %d of %d (%@): Writing...", @"text showing that app is writing data for table export"), (i + 1), tableCount, tableName]]; [exportProgressText displayIfNeeded]; @@ -496,11 +509,14 @@ [exportProgressIndicator setDoubleValue:0]; [exportProgressIndicator displayIfNeeded]; - // Start the actual export process in a separate thread using the supplied exporter instance - //[exporter startExportProcess]; + // Start the actual data conversion process by placing the exporter on the operation queue. + // Note that although it is highly likely there is no guarantee that the operation will executed + // as soon as it's placed on the queue. There may be a delay if the queue is already executing it's + // maximum number of concurrent operations. See the docs for more details. + [operationQueue addOperation:exporter]; // Add a spacer to the file - [fileHandle writeData:[[NSString stringWithFormat:@"%@%@%@", csvLineEnd, csvLineEnd, csvLineEnd] dataUsingEncoding:encoding]]; + //[fileHandle writeData:[[NSString stringWithFormat:@"%@%@%@", csvLineEnd, csvLineEnd, csvLineEnd] dataUsingEncoding:encoding]]; } // Close the progress sheet diff --git a/Source/SPExporter.h b/Source/SPExporter.h index febbcbc3..9bc2e592 100644 --- a/Source/SPExporter.h +++ b/Source/SPExporter.h @@ -55,20 +55,20 @@ @interface SPExporter : NSOperation { id delegate; + SEL didEndSelector; double exportProgressValue; BOOL exportProcessIsRunning; - BOOL exportProcessShouldExit; NSStringEncoding exportOutputEncoding; } @property (readwrite, assign) id delegate; +@property (readwrite, assign) SEL didEndSelector; @property (readwrite, assign) double exportProgressValue; @property (readwrite, assign) BOOL exportProcessIsRunning; -@property (readwrite, assign) BOOL exportProcessShouldExit; @property (readwrite, assign) NSStringEncoding exportOutputEncoding; diff --git a/Source/SPExporter.m b/Source/SPExporter.m index 9cc681d8..a5a1f834 100644 --- a/Source/SPExporter.m +++ b/Source/SPExporter.m @@ -28,9 +28,9 @@ @implementation SPExporter @synthesize delegate; +@synthesize didEndSelector; @synthesize exportProgressValue; @synthesize exportProcessIsRunning; -@synthesize exportProcessShouldExit; @synthesize exportOutputEncoding; /** @@ -41,7 +41,6 @@ if ((self == [super init])) { [self setExportProgressValue:0]; [self setExportProcessIsRunning:NO]; - [self setExportProcessShouldExit:NO]; // Default the output encoding to UTF-8 [self setExportOutputEncoding:NSUTF8StringEncoding]; diff --git a/Source/SPExporterAccess.h b/Source/SPExporterAccess.h deleted file mode 100644 index 484a9e69..00000000 --- a/Source/SPExporterAccess.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// $Id$ -// -// SPExporterAccess.h -// sequel-pro -// -// Created by Stuart Connolly (stuconnolly.com) on August 29, 2009 -// Copyright (c) 2009 Stuart Connolly. All rights reserved. -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// More info at - -#import - -@protocol SPExporterAccess - -/** - * Starts the export process. - */ -- (BOOL)startExportProcess; - -/** - * Stops the export process and passes control back to SPExportController. - */ -- (BOOL)stopExportProcess; - -@end diff --git a/Source/SPSQLExporter.h b/Source/SPSQLExporter.h index 3b5f3be3..7a2e9006 100644 --- a/Source/SPSQLExporter.h +++ b/Source/SPSQLExporter.h @@ -28,8 +28,5 @@ #import "SPExporter.h" @interface SPSQLExporter : SPExporter -{ - -} @end diff --git a/Source/SPSQLExporter.m b/Source/SPSQLExporter.m index 39653abc..db9e4532 100644 --- a/Source/SPSQLExporter.m +++ b/Source/SPSQLExporter.m @@ -27,4 +27,22 @@ @implementation SPSQLExporter +/** + * Start the SQL data conversion process. This method is automatically called when an instance of this object + * is placed on an NSOperationQueue. Do not call it directly as there is no manual multithreading. + */ +- (void)main +{ + @try { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + + + [pool release]; + } + @catch (NSException *e) { + + } +} + @end diff --git a/Source/SPXMLExporter.h b/Source/SPXMLExporter.h new file mode 100644 index 00000000..dba81cba --- /dev/null +++ b/Source/SPXMLExporter.h @@ -0,0 +1,32 @@ +// +// $Id$ +// +// SPSXMLExporter.h +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on October 6, 2009 +// Copyright (c) 2009 Stuart Connolly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import + +#import "SPExporter.h" + +@interface SPXMLExporter : SPExporter + +@end diff --git a/Source/SPXMLExporter.m b/Source/SPXMLExporter.m new file mode 100644 index 00000000..dabfe263 --- /dev/null +++ b/Source/SPXMLExporter.m @@ -0,0 +1,48 @@ +// +// $Id$ +// +// SPXMLExporter.h +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on October 6, 2009 +// Copyright (c) 2009 Stuart Connolly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "SPXMLExporter.h" + +@implementation SPXMLExporter + +/** + * Start the SQL data conversion process. This method is automatically called when an instance of this object + * is placed on an NSOperationQueue. Do not call it directly as there is no manual multithreading. + */ +- (void)main +{ + @try { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + + + [pool release]; + } + @catch (NSException *e) { + + } +} + +@end diff --git a/Source/TableDocument.h b/Source/TableDocument.h index 7ef7616d..b907ab37 100644 --- a/Source/TableDocument.h +++ b/Source/TableDocument.h @@ -54,7 +54,7 @@ enum sp_current_query_mode IBOutlet id extendedTableInfoInstance; IBOutlet id databaseDataInstance; IBOutlet id spHistoryControllerInstance; - IBOutlet id spExportControllerInstance; + IBOutlet id exportControllerInstance; IBOutlet NSSearchField *listFilterField; diff --git a/Source/TableDocument.m b/Source/TableDocument.m index b98d7d51..e8290043 100644 --- a/Source/TableDocument.m +++ b/Source/TableDocument.m @@ -587,18 +587,16 @@ [tableRelationsInstance setConnection:mySQLConnection]; [customQueryInstance setConnection:mySQLConnection]; [tableDumpInstance setConnection:mySQLConnection]; - [spExportControllerInstance setConnection:mySQLConnection]; + [exportControllerInstance setConnection:mySQLConnection]; [tableDataInstance setConnection:mySQLConnection]; [extendedTableInfoInstance setConnection:mySQLConnection]; [databaseDataInstance setConnection:mySQLConnection]; - if (userManagerInstance == nil) - { + + if (!userManagerInstance) { userManagerInstance = [[SPUserManager alloc] initWithConnection:mySQLConnection]; } - else - { - [userManagerInstance setConnection:mySQLConnection]; - } + + [userManagerInstance setConnection:mySQLConnection]; // Set the cutom query editor's MySQL version [customQueryInstance setMySQLversion:mySQLVersion]; @@ -2528,11 +2526,10 @@ */ - (IBAction)export:(id)sender { - if ([sender tag] == -1) { - //[tableDumpInstance export]; - - [spExportControllerInstance export]; - } else { + if ([sender tag] == -1) { + [exportControllerInstance export]; + } + else { [tableDumpInstance exportFile:[sender tag]]; } } -- cgit v1.2.3