diff options
-rw-r--r-- | Source/SPCSVExporter.m | 573 | ||||
-rw-r--r-- | Source/SPDotExporter.m | 239 | ||||
-rw-r--r-- | Source/SPHTMLExporter.m | 10 | ||||
-rw-r--r-- | Source/SPPDFExporter.m | 9 | ||||
-rw-r--r-- | Source/SPXMLExporter.m | 291 |
5 files changed, 553 insertions, 569 deletions
diff --git a/Source/SPCSVExporter.m b/Source/SPCSVExporter.m index fe6ebdac..b465b4e2 100644 --- a/Source/SPCSVExporter.m +++ b/Source/SPCSVExporter.m @@ -65,345 +65,342 @@ */ - (void)main { - @try { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSMutableString *csvString = [NSMutableString string]; - NSMutableString *csvCellString = [NSMutableString string]; - - NSMutableArray *tableColumnNumericStatus = [NSMutableArray array]; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSMutableString *csvString = [NSMutableString string]; + NSMutableString *csvCellString = [NSMutableString string]; + + NSMutableArray *tableColumnNumericStatus = [NSMutableArray array]; + + NSArray *csvRow = nil; + NSScanner *csvNumericTester = nil; + MCPStreamingResult *streamingResult = nil; + NSString *escapedEscapeString, *escapedFieldSeparatorString, *escapedEnclosingString, *escapedLineEndString, *dataConversionString; - NSArray *csvRow = nil; - NSScanner *csvNumericTester = nil; - MCPStreamingResult *streamingResult = nil; - NSString *escapedEscapeString, *escapedFieldSeparatorString, *escapedEnclosingString, *escapedLineEndString, *dataConversionString; + id csvCell; + BOOL csvCellIsNumeric; + BOOL quoteFieldSeparators = [[self csvEnclosingCharacterString] isEqualToString:@""]; - id csvCell; - BOOL csvCellIsNumeric; - BOOL quoteFieldSeparators = [[self csvEnclosingCharacterString] isEqualToString:@""]; + NSUInteger i, totalRows, lastProgressValue, csvCellCount = 0; - NSUInteger i, totalRows, lastProgressValue, csvCellCount = 0; - - // Check to see if we have at least a table name or data array - if ((![self csvTableName]) && (![self csvDataArray]) || - ([[self csvTableName] isEqualToString:@""]) && ([[self csvDataArray] count] == 0)) - { - [pool release]; - return; - } - - // Check that we have all the required info before starting the export - if ((![self csvOutputFieldNames]) || - (![self csvFieldSeparatorString]) || - (![self csvEscapeString]) || - (![self csvLineEndingString])) - { - [pool release]; - return; - } - - // Check that the CSV output options are not just empty strings - if (([[self csvFieldSeparatorString] isEqualToString:@""]) || - ([[self csvEscapeString] isEqualToString:@""]) || - ([[self csvLineEndingString] isEqualToString:@""])) - { - [pool release]; - return; - } - - // Inform the delegate that the export process is about to begin - [delegate performSelectorOnMainThread:@selector(csvExportProcessWillBegin:) withObject:self waitUntilDone:NO]; + // Check to see if we have at least a table name or data array + if ((![self csvTableName]) && (![self csvDataArray]) || + ([[self csvTableName] isEqualToString:@""]) && ([[self csvDataArray] count] == 0)) + { + [pool release]; + return; + } + + // Check that we have all the required info before starting the export + if ((![self csvOutputFieldNames]) || + (![self csvFieldSeparatorString]) || + (![self csvEscapeString]) || + (![self csvLineEndingString])) + { + [pool release]; + return; + } + + // Check that the CSV output options are not just empty strings + if (([[self csvFieldSeparatorString] isEqualToString:@""]) || + ([[self csvEscapeString] isEqualToString:@""]) || + ([[self csvLineEndingString] isEqualToString:@""])) + { + [pool release]; + return; + } - // Mark the process as running - [self setExportProcessIsRunning:YES]; - - lastProgressValue = 0; - - // Make a streaming request for the data if the data array isn't set - if ((![self csvDataArray]) && [self csvTableName]) { - streamingResult = [connection streamingQueryString:[NSString stringWithFormat:@"SELECT * FROM %@", [[self csvTableName] backtickQuotedString]] useLowMemoryBlockingStreaming:[self exportUsingLowMemoryBlockingStreaming]]; - } - - // 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]]; + // Inform the delegate that the export process is about to begin + [delegate performSelectorOnMainThread:@selector(csvExportProcessWillBegin:) withObject:self waitUntilDone:NO]; + + // Mark the process as running + [self setExportProcessIsRunning:YES]; + + lastProgressValue = 0; + + // Make a streaming request for the data if the data array isn't set + if ((![self csvDataArray]) && [self csvTableName]) { + streamingResult = [connection streamingQueryString:[NSString stringWithFormat:@"SELECT * FROM %@", [[self csvTableName] backtickQuotedString]] useLowMemoryBlockingStreaming:[self exportUsingLowMemoryBlockingStreaming]]; + } + + // 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]]; + + // Set up the starting row; for supplied arrays, which include the column + // headers as the first row, decide whether to skip the first row. + NSUInteger currentRowIndex = 0; + + [csvString setString:@""]; + + if ([self csvDataArray]) totalRows = [[self csvDataArray] count]; + if (([self csvDataArray]) && (![self csvOutputFieldNames])) currentRowIndex++; + + if ([self csvTableName] && (![self csvDataArray])) { - // Set up the starting row; for supplied arrays, which include the column - // headers as the first row, decide whether to skip the first row. - NSUInteger currentRowIndex = 0; + NSDictionary *tableDetails; - [csvString setString:@""]; + // Determine whether the supplied table is actually a table or a view via the CREATE TABLE command, and get the table details + MCPResult *queryResult = [connection queryString:[NSString stringWithFormat:@"SHOW CREATE TABLE %@", [[self csvTableName] backtickQuotedString]]]; - if ([self csvDataArray]) totalRows = [[self csvDataArray] count]; - if (([self csvDataArray]) && (![self csvOutputFieldNames])) currentRowIndex++; + [queryResult setReturnDataAsStrings:YES]; - if ([self csvTableName] && (![self csvDataArray])) { + if ([queryResult numOfRows]) { + tableDetails = [[NSDictionary alloc] initWithDictionary:[queryResult fetchRowAsDictionary]]; - NSDictionary *tableDetails; - - // Determine whether the supplied table is actually a table or a view via the CREATE TABLE command, and get the table details - MCPResult *queryResult = [connection queryString:[NSString stringWithFormat:@"SHOW CREATE TABLE %@", [[self csvTableName] backtickQuotedString]]]; - - [queryResult setReturnDataAsStrings:YES]; + tableDetails = [[NSDictionary alloc] initWithDictionary:([tableDetails objectForKey:@"Create View"]) ? [[self csvTableData] informationForView:[self csvTableName]] : [[self csvTableData] informationForTable:[self csvTableName]]]; + } + + // Retrieve the table details via the data class, and use it to build an array containing column numeric status + for (NSDictionary *column in [tableDetails objectForKey:@"columns"]) + { + NSString *tableColumnTypeGrouping = [column objectForKey:@"typegrouping"]; - if ([queryResult numOfRows]) { - tableDetails = [[NSDictionary alloc] initWithDictionary:[queryResult fetchRowAsDictionary]]; - - tableDetails = [[NSDictionary alloc] initWithDictionary:([tableDetails objectForKey:@"Create View"]) ? [[self csvTableData] informationForView:[self csvTableName]] : [[self csvTableData] informationForTable:[self csvTableName]]]; + [tableColumnNumericStatus addObject:[NSNumber numberWithBool:([tableColumnTypeGrouping isEqualToString:@"bit"] || + [tableColumnTypeGrouping isEqualToString:@"integer"] || + [tableColumnTypeGrouping isEqualToString:@"float"])]]; + } + + [tableDetails release]; + } + + // Drop into the processing loop + NSAutoreleasePool *csvExportPool = [[NSAutoreleasePool alloc] init]; + + NSUInteger currentPoolDataLength = 0; + + // Inform the delegate that we are about to start writing the data to disk + [delegate performSelectorOnMainThread:@selector(csvExportProcessWillBeginWritingData:) withObject:self waitUntilDone:NO]; + + while (1) + { + // Check for cancellation flag + if ([self isCancelled]) { + if (streamingResult) { + [connection cancelCurrentQuery]; + [streamingResult cancelResultLoad]; } - // Retrieve the table details via the data class, and use it to build an array containing column numeric status - for (NSDictionary *column in [tableDetails objectForKey:@"columns"]) - { - NSString *tableColumnTypeGrouping = [column objectForKey:@"typegrouping"]; - - [tableColumnNumericStatus addObject:[NSNumber numberWithBool:([tableColumnTypeGrouping isEqualToString:@"bit"] || - [tableColumnTypeGrouping isEqualToString:@"integer"] || - [tableColumnTypeGrouping isEqualToString:@"float"])]]; - } + [csvExportPool release]; + [pool release]; - [tableDetails release]; + return; } - // Drop into the processing loop - NSAutoreleasePool *csvExportPool = [[NSAutoreleasePool alloc] init]; - - NSUInteger currentPoolDataLength = 0; + // Retrieve the next row from the supplied data, either directly from the array... + if ([self csvDataArray]) { + csvRow = NSArrayObjectAtIndex([self csvDataArray], currentRowIndex); + } + // Or by reading an appropriate row from the streaming result + else { + // If still requested to read the field names, get the field names + if ([self csvOutputFieldNames]) { + csvRow = [streamingResult fetchFieldNames]; + [self setCsvOutputFieldNames:NO]; + } + else { + csvRow = [streamingResult fetchNextRowAsArray]; + + if (!csvRow) break; + } + } - // Inform the delegate that we are about to start writing the data to disk - [delegate performSelectorOnMainThread:@selector(csvExportProcessWillBeginWritingData:) withObject:self waitUntilDone:NO]; + // Get the cell count if we don't already have it stored + if (!csvCellCount) csvCellCount = [csvRow count]; + + [csvString setString:@""]; - while (1) + for (i = 0 ; i < csvCellCount; i++) { // Check for cancellation flag if ([self isCancelled]) { - if (streamingResult) { - [connection cancelCurrentQuery]; - [streamingResult cancelResultLoad]; - } - [csvExportPool release]; [pool release]; return; } - // Retrieve the next row from the supplied data, either directly from the array... - if ([self csvDataArray]) { - csvRow = NSArrayObjectAtIndex([self csvDataArray], currentRowIndex); - } - // Or by reading an appropriate row from the streaming result - else { - // If still requested to read the field names, get the field names - if ([self csvOutputFieldNames]) { - csvRow = [streamingResult fetchFieldNames]; - [self setCsvOutputFieldNames:NO]; - } - else { - csvRow = [streamingResult fetchNextRowAsArray]; - - if (!csvRow) break; - } + csvCell = NSArrayObjectAtIndex(csvRow, i); + + // For NULL objects supplied from a queryResult, add an unenclosed null string as per prefs + if ([csvCell isKindOfClass:[NSNull class]]) { + [csvString appendString:[self csvNULLString]]; + + if (i < (csvCellCount - 1)) [csvString appendString:[self csvFieldSeparatorString]]; + + continue; } - // Get the cell count if we don't already have it stored - if (!csvCellCount) csvCellCount = [csvRow count]; - - [csvString setString:@""]; - - for (i = 0 ; i < csvCellCount; i++) - { - // Check for cancellation flag - if ([self isCancelled]) { - [csvExportPool release]; - [pool release]; - - return; - } + // Retrieve the contents of this cell + if ([csvCell isKindOfClass:[NSData class]]) { + dataConversionString = [[NSString alloc] initWithData:csvCell encoding:[self exportOutputEncoding]]; - csvCell = NSArrayObjectAtIndex(csvRow, i); - - // For NULL objects supplied from a queryResult, add an unenclosed null string as per prefs - if ([csvCell isKindOfClass:[NSNull class]]) { - [csvString appendString:[self csvNULLString]]; - - if (i < (csvCellCount - 1)) [csvString appendString:[self csvFieldSeparatorString]]; - - continue; + if (dataConversionString == nil) { + dataConversionString = [[NSString alloc] initWithData:csvCell encoding:NSASCIIStringEncoding]; } - // Retrieve the contents of this cell - if ([csvCell isKindOfClass:[NSData class]]) { - dataConversionString = [[NSString alloc] initWithData:csvCell encoding:[self exportOutputEncoding]]; - - if (dataConversionString == nil) { - dataConversionString = [[NSString alloc] initWithData:csvCell encoding:NSASCIIStringEncoding]; - } - - [csvCellString setString:[NSString stringWithString:dataConversionString]]; - [dataConversionString release]; + [csvCellString setString:[NSString stringWithString:dataConversionString]]; + [dataConversionString release]; + } + else { + [csvCellString setString:[csvCell description]]; + } + + // For NULL values supplied via an array add the unenclosed null string as set in preferences + if ([csvCellString isEqualToString:[self csvNULLString]]) { + [csvString appendString:[self csvNULLString]]; + } + // Add empty strings as a pair of enclosing characters. + else if ([csvCellString length] == 0) { + [csvString appendString:[self csvEnclosingCharacterString]]; + [csvString appendString:[self csvEnclosingCharacterString]]; + } + else { + // If an array of bools supplying information as to whether the column is numeric has been supplied, use it. + if ([tableColumnNumericStatus count] > 0) { + csvCellIsNumeric = [NSArrayObjectAtIndex(tableColumnNumericStatus, i) boolValue]; + } + // Otherwise, first test whether this cell contains data + else if ([NSArrayObjectAtIndex(csvRow, i) isKindOfClass:[NSData class]]) { + csvCellIsNumeric = NO; } + // Or fall back to testing numeric content via an NSScanner. else { - [csvCellString setString:[csvCell description]]; + csvNumericTester = [NSScanner scannerWithString:csvCellString]; + + csvCellIsNumeric = [csvNumericTester scanFloat:nil] && + [csvNumericTester isAtEnd] && + ([csvCellString characterAtIndex:0] != '0' || + [csvCellString length] == 1 || + ([csvCellString length] > 1 && + [csvCellString characterAtIndex:1] == '.')); } + + // Escape any occurrences of the escaping character + [csvCellString replaceOccurrencesOfString:[self csvEscapeString] + withString:escapedEscapeString + options:NSLiteralSearch + range:NSMakeRange(0, [csvCellString length])]; - // For NULL values supplied via an array add the unenclosed null string as set in preferences - if ([csvCellString isEqualToString:[self csvNULLString]]) { - [csvString appendString:[self csvNULLString]]; - } - // Add empty strings as a pair of enclosing characters. - else if ([csvCellString length] == 0) { - [csvString appendString:[self csvEnclosingCharacterString]]; - [csvString appendString:[self csvEnclosingCharacterString]]; - } - else { - // If an array of bools supplying information as to whether the column is numeric has been supplied, use it. - if ([tableColumnNumericStatus count] > 0) { - csvCellIsNumeric = [NSArrayObjectAtIndex(tableColumnNumericStatus, i) boolValue]; - } - // Otherwise, first test whether this cell contains data - else if ([NSArrayObjectAtIndex(csvRow, i) isKindOfClass:[NSData class]]) { - csvCellIsNumeric = NO; - } - // Or fall back to testing numeric content via an NSScanner. - else { - csvNumericTester = [NSScanner scannerWithString:csvCellString]; - - csvCellIsNumeric = [csvNumericTester scanFloat:nil] && - [csvNumericTester isAtEnd] && - ([csvCellString characterAtIndex:0] != '0' || - [csvCellString length] == 1 || - ([csvCellString length] > 1 && - [csvCellString characterAtIndex:1] == '.')); - } - - // Escape any occurrences of the escaping character - [csvCellString replaceOccurrencesOfString:[self csvEscapeString] - withString:escapedEscapeString + // Escape any occurrences of the enclosure string + if (![[self csvEscapeString] isEqualToString:[self csvEnclosingCharacterString]]) { + [csvCellString replaceOccurrencesOfString:[self csvEnclosingCharacterString] + withString:escapedEnclosingString options:NSLiteralSearch range:NSMakeRange(0, [csvCellString length])]; - - // Escape any occurrences of the enclosure string - if (![[self csvEscapeString] isEqualToString:[self csvEnclosingCharacterString]]) { - [csvCellString replaceOccurrencesOfString:[self csvEnclosingCharacterString] - withString:escapedEnclosingString - options:NSLiteralSearch - range:NSMakeRange(0, [csvCellString length])]; - } - - // Escape occurrences of the line end character - [csvCellString replaceOccurrencesOfString:[self csvLineEndingString] - withString:escapedLineEndString + } + + // Escape occurrences of the line end character + [csvCellString replaceOccurrencesOfString:[self csvLineEndingString] + withString:escapedLineEndString + options:NSLiteralSearch + range:NSMakeRange(0, [csvCellString length])]; + + // If the string isn't quoted or otherwise enclosed, escape occurrences of the field separators + if (quoteFieldSeparators || csvCellIsNumeric) { + [csvCellString replaceOccurrencesOfString:[self csvFieldSeparatorString] + withString:escapedFieldSeparatorString options:NSLiteralSearch range:NSMakeRange(0, [csvCellString length])]; - - // If the string isn't quoted or otherwise enclosed, escape occurrences of the field separators - if (quoteFieldSeparators || csvCellIsNumeric) { - [csvCellString replaceOccurrencesOfString:[self csvFieldSeparatorString] - withString:escapedFieldSeparatorString - options:NSLiteralSearch - range:NSMakeRange(0, [csvCellString length])]; - } - - // Write out the cell data by appending strings - this is significantly faster than stringWithFormat. - if (csvCellIsNumeric) { - [csvString appendString:csvCellString]; - } - else { - [csvString appendString:[self csvEnclosingCharacterString]]; - [csvString appendString:csvCellString]; - [csvString appendString:[self csvEnclosingCharacterString]]; - } } - if (i < ([csvRow count] - 1)) [csvString appendString:[self csvFieldSeparatorString]]; + // Write out the cell data by appending strings - this is significantly faster than stringWithFormat. + if (csvCellIsNumeric) { + [csvString appendString:csvCellString]; + } + else { + [csvString appendString:[self csvEnclosingCharacterString]]; + [csvString appendString:csvCellString]; + [csvString appendString:[self csvEnclosingCharacterString]]; + } } - // Append the line ending to the string for this row, and record the length processed for pool flushing - [csvString appendString:[self csvLineEndingString]]; - currentPoolDataLength += [csvString length]; - - // Write it to the fileHandle - [[self exportOutputFileHandle] writeData:[csvString dataUsingEncoding:[self exportOutputEncoding]]]; - - currentRowIndex++; - - // Update the progress - if (totalRows && (currentRowIndex * ([self exportMaxProgress] / totalRows)) > lastProgressValue) { - - NSInteger progress = (currentRowIndex * ([self exportMaxProgress] / totalRows)); - - [self setExportProgressValue:progress]; - - lastProgressValue = progress; - } + if (i < ([csvRow count] - 1)) [csvString appendString:[self csvFieldSeparatorString]]; + } + + // Append the line ending to the string for this row, and record the length processed for pool flushing + [csvString appendString:[self csvLineEndingString]]; + currentPoolDataLength += [csvString length]; + + // Write it to the fileHandle + [[self exportOutputFileHandle] writeData:[csvString dataUsingEncoding:[self exportOutputEncoding]]]; + + currentRowIndex++; + + // Update the progress + if (totalRows && (currentRowIndex * ([self exportMaxProgress] / totalRows)) > lastProgressValue) { - // Inform the delegate that the export's progress has been updated - [delegate performSelectorOnMainThread:@selector(csvExportProcessProgressUpdated:) withObject:self waitUntilDone:NO]; + NSInteger progress = (currentRowIndex * ([self exportMaxProgress] / totalRows)); - // Drain the autorelease pool as required to keep memory usage low - if (currentPoolDataLength > 250000) { - [csvExportPool release]; - csvExportPool = [[NSAutoreleasePool alloc] init]; - } + [self setExportProgressValue:progress]; - // If an array was supplied and we've processed all rows, break - if ([self csvDataArray] && (totalRows == currentRowIndex)) break; + lastProgressValue = progress; } - // Write data to disk - [[self exportOutputFileHandle] synchronizeFile]; - - // Mark the process as not running - [self setExportProcessIsRunning:NO]; + // Inform the delegate that the export's progress has been updated + [delegate performSelectorOnMainThread:@selector(csvExportProcessProgressUpdated:) withObject:self waitUntilDone:NO]; - // Inform the delegate that the export process is complete - [delegate performSelectorOnMainThread:@selector(csvExportProcessComplete:) withObject:self waitUntilDone:NO]; + // Drain the autorelease pool as required to keep memory usage low + if (currentPoolDataLength > 250000) { + [csvExportPool release]; + csvExportPool = [[NSAutoreleasePool alloc] init]; + } - [csvExportPool release]; - [pool release]; + // If an array was supplied and we've processed all rows, break + if ([self csvDataArray] && (totalRows == currentRowIndex)) break; } - @catch(NSException *e) {} + + // Write data to disk + [[self exportOutputFileHandle] synchronizeFile]; + + // Mark the process as not running + [self setExportProcessIsRunning:NO]; + + // Inform the delegate that the export process is complete + [delegate performSelectorOnMainThread:@selector(csvExportProcessComplete:) withObject:self waitUntilDone:NO]; + + [csvExportPool release]; + [pool release]; } /** diff --git a/Source/SPDotExporter.m b/Source/SPDotExporter.m index cbb132d9..044065e5 100644 --- a/Source/SPDotExporter.m +++ b/Source/SPDotExporter.m @@ -61,150 +61,147 @@ */ - (void)main { - @try { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSMutableString *metaString = [NSMutableString string]; - - // Check that we have all the required info before starting the export - if ((![self dotExportTables]) || (![self dotTableData]) || ([[self dotExportTables] count] == 0)) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSMutableString *metaString = [NSMutableString string]; + + // Check that we have all the required info before starting the export + if ((![self dotExportTables]) || (![self dotTableData]) || ([[self dotExportTables] count] == 0)) { + [pool release]; + return; + } + + // Inform the delegate that the export process is about to begin + [delegate performSelectorOnMainThread:@selector(dotExportProcessWillBegin:) withObject:self waitUntilDone:NO]; + + // Mark the process as running + [self setExportProcessIsRunning:YES]; + + [metaString setString:@"// ************************************************************\n"]; + [metaString appendString:@"// Generated by: Sequel Pro\n"]; + [metaString appendString:[NSString stringWithFormat:@"// Version %@\n//\n", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; + [metaString appendString:[NSString stringWithFormat:@"// %@\n// %@\n//\n", SPHomePageURL, SPDevURL]]; + [metaString appendString:[NSString stringWithFormat:@"// Host: %@ (MySQL %@)\n", [self dotDatabaseHost], [self dotDatabaseVersion]]]; + [metaString appendString:[NSString stringWithFormat:@"// Database: %@\n", [self dotDatabaseName]]]; + [metaString appendString:[NSString stringWithFormat:@"// Generation Time: %@\n", [NSDate date]]]; + [metaString appendString:@"// ************************************************************\n\n"]; + + [metaString appendString:@"digraph \"Database Structure\" {\n"]; + [metaString appendString:[NSString stringWithFormat:@"\tlabel = \"ER Diagram: %@\";\n", [self dotDatabaseName]]]; + [metaString appendString:@"\tlabelloc = t;\n"]; + [metaString appendString:@"\tcompound = true;\n"]; + [metaString appendString:@"\tnode [ shape = record ];\n"]; + [metaString appendString:@"\tfontname = \"Helvetica\";\n"]; + [metaString appendString:@"\tranksep = 1.25;\n"]; + [metaString appendString:@"\tratio = 0.7;\n"]; + [metaString appendString:@"\trankdir = LR;\n"]; + + // Write information to the file + [[self exportOutputFileHandle] writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]]; + + NSMutableArray *fkInfo = [[NSMutableArray alloc] init]; + + // Process the tables + for (NSInteger i = 0; i < [[self dotExportTables] count]; i++) + { + // Check for cancellation flag + if ([self isCancelled]) { [pool release]; return; } + + NSString *tableName = NSArrayObjectAtIndex([self dotExportTables], i); + NSDictionary *tableInfo = [[self dotTableData] informationForTable:tableName]; + + // Set the current table + [self setDotExportCurrentTable:tableName]; + + // Inform the delegate that we are about to start fetcihing data for the current table + [delegate performSelectorOnMainThread:@selector(dotExportProcessWillBeginFetchingData:) withObject:self waitUntilDone:NO]; + + NSString *hdrColor = @"#DDDDDD"; + + if ([[tableInfo objectForKey:@"type"] isEqualToString:@"View"]) { + hdrColor = @"#DDDDFF"; + } - // Inform the delegate that the export process is about to begin - [delegate performSelectorOnMainThread:@selector(dotExportProcessWillBegin:) withObject:self waitUntilDone:NO]; + [metaString setString:[NSString stringWithFormat:@"\tsubgraph \"table_%@\" {\n", tableName]]; + [metaString appendString:@"\t\tnode [ shape = \"plaintext\" ];\n"]; + [metaString appendString:[NSString stringWithFormat:@"\t\t\"%@\" [ label=<\n", tableName]]; + [metaString appendString:@"\t\t\t<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLBORDER=\"1\">\n"]; + [metaString appendString:[NSString stringWithFormat:@"\t\t\t<TR><TD COLSPAN=\"3\" BGCOLOR=\"%@\">%@</TD></TR>\n", hdrColor, tableName]]; - // Mark the process as running - [self setExportProcessIsRunning:YES]; + // Get the column info + NSArray *columnInfo = [tableInfo objectForKey:@"columns"]; - [metaString setString:@"// ************************************************************\n"]; - [metaString appendString:@"// Generated by: Sequel Pro\n"]; - [metaString appendString:[NSString stringWithFormat:@"// Version %@\n//\n", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; - [metaString appendString:[NSString stringWithFormat:@"// %@\n// %@\n//\n", SPHomePageURL, SPDevURL]]; - [metaString appendString:[NSString stringWithFormat:@"// Host: %@ (MySQL %@)\n", [self dotDatabaseHost], [self dotDatabaseVersion]]]; - [metaString appendString:[NSString stringWithFormat:@"// Database: %@\n", [self dotDatabaseName]]]; - [metaString appendString:[NSString stringWithFormat:@"// Generation Time: %@\n", [NSDate date]]]; - [metaString appendString:@"// ************************************************************\n\n"]; + for (NSInteger j = 0; j < [columnInfo count]; j++ ) + { + [metaString appendString:[NSString stringWithFormat:@"\t\t\t<TR><TD COLSPAN=\"3\" PORT=\"%@\">%@:<FONT FACE=\"Helvetica-Oblique\" POINT-SIZE=\"10\">%@</FONT></TD></TR>\n", [[columnInfo objectAtIndex:j] objectForKey:@"name"], [[columnInfo objectAtIndex:j] objectForKey:@"name"], [[columnInfo objectAtIndex:j] objectForKey:@"type"]]]; + } - [metaString appendString:@"digraph \"Database Structure\" {\n"]; - [metaString appendString:[NSString stringWithFormat:@"\tlabel = \"ER Diagram: %@\";\n", [self dotDatabaseName]]]; - [metaString appendString:@"\tlabelloc = t;\n"]; - [metaString appendString:@"\tcompound = true;\n"]; - [metaString appendString:@"\tnode [ shape = record ];\n"]; - [metaString appendString:@"\tfontname = \"Helvetica\";\n"]; - [metaString appendString:@"\tranksep = 1.25;\n"]; - [metaString appendString:@"\tratio = 0.7;\n"]; - [metaString appendString:@"\trankdir = LR;\n"]; + [metaString appendString:@"\t\t\t</TABLE>>\n"]; + [metaString appendString:@"\t\t];\n"]; + [metaString appendString:@"\t}\n"]; - // Write information to the file [[self exportOutputFileHandle] writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]]; - - NSMutableArray *fkInfo = [[NSMutableArray alloc] init]; - // Process the tables - for (NSInteger i = 0; i < [[self dotExportTables] count]; i++) + // see about relations + columnInfo = [tableInfo objectForKey:@"constraints"]; + + for (NSInteger j = 0; j < [columnInfo count]; j++) { // Check for cancellation flag if ([self isCancelled]) { [pool release]; return; } - - NSString *tableName = NSArrayObjectAtIndex([self dotExportTables], i); - NSDictionary *tableInfo = [[self dotTableData] informationForTable:tableName]; - - // Set the current table - [self setDotExportCurrentTable:tableName]; - - // Inform the delegate that we are about to start fetcihing data for the current table - [delegate performSelectorOnMainThread:@selector(dotExportProcessWillBeginFetchingData:) withObject:self waitUntilDone:NO]; - - NSString *hdrColor = @"#DDDDDD"; - - if ([[tableInfo objectForKey:@"type"] isEqualToString:@"View"]) { - hdrColor = @"#DDDDFF"; - } - [metaString setString:[NSString stringWithFormat:@"\tsubgraph \"table_%@\" {\n", tableName]]; - [metaString appendString:@"\t\tnode [ shape = \"plaintext\" ];\n"]; - [metaString appendString:[NSString stringWithFormat:@"\t\t\"%@\" [ label=<\n", tableName]]; - [metaString appendString:@"\t\t\t<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLBORDER=\"1\">\n"]; - [metaString appendString:[NSString stringWithFormat:@"\t\t\t<TR><TD COLSPAN=\"3\" BGCOLOR=\"%@\">%@</TD></TR>\n", hdrColor, tableName]]; + // Get the column references. Currently the columns themselves are an array, + // while reference columns and tables are comma separated if there are more than + // one. Only use the first of each for the time being. + NSArray *ccols = [NSArrayObjectAtIndex(columnInfo, j) objectForKey:@"columns"]; + NSString *ccol = NSArrayObjectAtIndex(columnInfo, 0); + NSString *rcol = [NSArrayObjectAtIndex(columnInfo, j) objectForKey:@"ref_columns"]; - // Get the column info - NSArray *columnInfo = [tableInfo objectForKey:@"columns"]; + NSString *extra = @""; - for (NSInteger j = 0; j < [columnInfo count]; j++ ) - { - [metaString appendString:[NSString stringWithFormat:@"\t\t\t<TR><TD COLSPAN=\"3\" PORT=\"%@\">%@:<FONT FACE=\"Helvetica-Oblique\" POINT-SIZE=\"10\">%@</FONT></TD></TR>\n", [[columnInfo objectAtIndex:j] objectForKey:@"name"], [[columnInfo objectAtIndex:j] objectForKey:@"name"], [[columnInfo objectAtIndex:j] objectForKey:@"type"]]]; + if ([ccols count] > 1) { + extra = @" [ arrowhead=crow, arrowtail=odiamond ]"; + rcol = NSArrayObjectAtIndex([rcol componentsSeparatedByString:@","], 0); } - [metaString appendString:@"\t\t\t</TABLE>>\n"]; - [metaString appendString:@"\t\t];\n"]; - [metaString appendString:@"\t}\n"]; - - [[self exportOutputFileHandle] writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]]; - - // see about relations - columnInfo = [tableInfo objectForKey:@"constraints"]; - - for (NSInteger j = 0; j < [columnInfo count]; j++) - { - // Check for cancellation flag - if ([self isCancelled]) { - [pool release]; - return; - } - - // Get the column references. Currently the columns themselves are an array, - // while reference columns and tables are comma separated if there are more than - // one. Only use the first of each for the time being. - NSArray *ccols = [NSArrayObjectAtIndex(columnInfo, j) objectForKey:@"columns"]; - NSString *ccol = NSArrayObjectAtIndex(columnInfo, 0); - NSString *rcol = [NSArrayObjectAtIndex(columnInfo, j) objectForKey:@"ref_columns"]; - - NSString *extra = @""; - - if ([ccols count] > 1) { - extra = @" [ arrowhead=crow, arrowtail=odiamond ]"; - rcol = NSArrayObjectAtIndex([rcol componentsSeparatedByString:@","], 0); - } - - [fkInfo addObject:[NSString stringWithFormat:@"%@:%@ -> %@:%@ %@", tableName, ccol, [NSArrayObjectAtIndex(columnInfo, j) objectForKey:@"ref_table"], rcol, extra]]; - } + [fkInfo addObject:[NSString stringWithFormat:@"%@:%@ -> %@:%@ %@", tableName, ccol, [NSArrayObjectAtIndex(columnInfo, j) objectForKey:@"ref_table"], rcol, extra]]; } - - // Inform the delegate that we are about to start fetching relations data for the current table - [delegate performSelectorOnMainThread:@selector(dotExportProcessWillBeginFetchingRelationsData:) withObject:self waitUntilDone:NO]; - - [metaString setString:@"edge [ arrowhead=inv, arrowtail=normal, style=dashed, color=\"#444444\" ];\n"]; - - // Get the relations - for (NSInteger i = 0; i < [fkInfo count]; i++) - { - [metaString appendString:[NSString stringWithFormat:@"%@;\n", [fkInfo objectAtIndex:i]]]; - } - - [fkInfo release]; - - [metaString appendString:@"}\n"]; - - // Write information to the file - [[self exportOutputFileHandle] writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]]; - - // Write data to disk - [[self exportOutputFileHandle] closeFile]; - - // Mark the process as not running - [self setExportProcessIsRunning:NO]; - - // Inform the delegate that the export process is complete - [delegate performSelectorOnMainThread:@selector(dotExportProcessComplete:) withObject:self waitUntilDone:NO]; - - [pool release]; } - @catch (NSException *e) { } + + // Inform the delegate that we are about to start fetching relations data for the current table + [delegate performSelectorOnMainThread:@selector(dotExportProcessWillBeginFetchingRelationsData:) withObject:self waitUntilDone:NO]; + + [metaString setString:@"edge [ arrowhead=inv, arrowtail=normal, style=dashed, color=\"#444444\" ];\n"]; + + // Get the relations + for (NSInteger i = 0; i < [fkInfo count]; i++) + { + [metaString appendString:[NSString stringWithFormat:@"%@;\n", [fkInfo objectAtIndex:i]]]; + } + + [fkInfo release]; + + [metaString appendString:@"}\n"]; + + // Write information to the file + [[self exportOutputFileHandle] writeData:[metaString dataUsingEncoding:NSUTF8StringEncoding]]; + + // Write data to disk + [[self exportOutputFileHandle] closeFile]; + + // Mark the process as not running + [self setExportProcessIsRunning:NO]; + + // Inform the delegate that the export process is complete + [delegate performSelectorOnMainThread:@selector(dotExportProcessComplete:) withObject:self waitUntilDone:NO]; + + [pool release]; } /** diff --git a/Source/SPHTMLExporter.m b/Source/SPHTMLExporter.m index 45f41c54..dbf649dc 100644 --- a/Source/SPHTMLExporter.m +++ b/Source/SPHTMLExporter.m @@ -50,13 +50,9 @@ */ - (void)main { - @try { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - [pool release]; - - } - @catch (NSException *e) { } + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + [pool release]; } @end diff --git a/Source/SPPDFExporter.m b/Source/SPPDFExporter.m index a850e2bf..725b4173 100644 --- a/Source/SPPDFExporter.m +++ b/Source/SPPDFExporter.m @@ -50,12 +50,9 @@ */ - (void)main { - @try { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - [pool release]; - } - @catch (NSException *e) { } + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + [pool release]; } @end diff --git a/Source/SPXMLExporter.m b/Source/SPXMLExporter.m index de43560e..0c060a55 100644 --- a/Source/SPXMLExporter.m +++ b/Source/SPXMLExporter.m @@ -58,73 +58,104 @@ */ - (void)main { - @try { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSArray *xmlRow = nil; - NSString *dataConversionString = nil; - MCPStreamingResult *streamingResult = nil; - - NSMutableArray *xmlTags = [NSMutableArray array]; - NSMutableString *xmlString = [NSMutableString string]; - NSMutableString *xmlItem = [NSMutableString string]; - - NSUInteger xmlRowCount = 0; - NSUInteger i, totalRows, currentRowIndex, lastProgressValue, currentPoolDataLength; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSArray *xmlRow = nil; + NSString *dataConversionString = nil; + MCPStreamingResult *streamingResult = nil; + + NSMutableArray *xmlTags = [NSMutableArray array]; + NSMutableString *xmlString = [NSMutableString string]; + NSMutableString *xmlItem = [NSMutableString string]; + + NSUInteger xmlRowCount = 0; + NSUInteger i, totalRows, currentRowIndex, lastProgressValue, currentPoolDataLength; + + // Check to see if we have at least a table name or data array + if ((![self xmlTableName]) && (![self xmlDataArray]) || + ([[self xmlTableName] isEqualToString:@""]) && ([[self xmlDataArray] count] == 0)) + { + [pool release]; + return; + } + + // Inform the delegate that the export process is about to begin + [delegate performSelectorOnMainThread:@selector(xmlExportProcessWillBegin:) withObject:self waitUntilDone:NO]; + + // Mark the process as running + [self setExportProcessIsRunning:YES]; + + lastProgressValue = 0; + + // Make a streaming request for the data if the data array isn't set + if ((![self xmlDataArray]) && [self xmlTableName]) { + totalRows = [[[[connection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@", [[self xmlTableName] backtickQuotedString]]] fetchRowAsArray] objectAtIndex:0] integerValue]; + streamingResult = [connection streamingQueryString:[NSString stringWithFormat:@"SELECT * FROM %@", [[self xmlTableName] backtickQuotedString]] useLowMemoryBlockingStreaming:[self exportUsingLowMemoryBlockingStreaming]]; + } + else { + totalRows = [[self xmlDataArray] count]; + } + + // Set up an array of encoded field names as opening and closing tags + xmlRow = ([self xmlDataArray]) ? [[self xmlDataArray] objectAtIndex:0] : [streamingResult fetchFieldNames]; + + for (i = 0; i < [xmlRow count]; i++) + { + [xmlTags addObject:[NSMutableArray array]]; - // Check to see if we have at least a table name or data array - if ((![self xmlTableName]) && (![self xmlDataArray]) || - ([[self xmlTableName] isEqualToString:@""]) && ([[self xmlDataArray] count] == 0)) - { + [[xmlTags objectAtIndex:i] addObject:[NSString stringWithFormat:@"\t\t<%@>", [[[xmlRow objectAtIndex:i] description] HTMLEscapeString]]]; + [[xmlTags objectAtIndex:i] addObject:[NSString stringWithFormat:@"</%@>\n", [[[xmlRow objectAtIndex:i] description] HTMLEscapeString]]]; + } + + [[self exportOutputFileHandle] writeData:[xmlString dataUsingEncoding:[self exportOutputEncoding]]]; + + // Write an opening tag in the form of the table name + [[self exportOutputFileHandle] writeData:[[NSString stringWithFormat:@"\t<%@>\n", ([self xmlTableName]) ? [[self xmlTableName] HTMLEscapeString] : @"custom"] dataUsingEncoding:[self exportOutputEncoding]]]; + + // Set up the starting row, which is 0 for streaming result sets and + // 1 for supplied arrays which include the column headers as the first row. + currentRowIndex = 0; + + if ([self xmlDataArray]) currentRowIndex++; + + // Drop into the processing loop + NSAutoreleasePool *xmlExportPool = [[NSAutoreleasePool alloc] init]; + + currentPoolDataLength = 0; + + while (1) + { + // Check for cancellation flag + if ([self isCancelled]) { + if (streamingResult) { + [connection cancelCurrentQuery]; + [streamingResult cancelResultLoad]; + } + + [xmlExportPool release]; [pool release]; + return; } - - // Inform the delegate that the export process is about to begin - [delegate performSelectorOnMainThread:@selector(xmlExportProcessWillBegin:) withObject:self waitUntilDone:NO]; - // Mark the process as running - [self setExportProcessIsRunning:YES]; - - lastProgressValue = 0; - - // Make a streaming request for the data if the data array isn't set - if ((![self xmlDataArray]) && [self xmlTableName]) { - totalRows = [[[[connection queryString:[NSString stringWithFormat:@"SELECT COUNT(1) FROM %@", [[self xmlTableName] backtickQuotedString]]] fetchRowAsArray] objectAtIndex:0] integerValue]; - streamingResult = [connection streamingQueryString:[NSString stringWithFormat:@"SELECT * FROM %@", [[self xmlTableName] backtickQuotedString]] useLowMemoryBlockingStreaming:[self exportUsingLowMemoryBlockingStreaming]]; - } + // Retrieve the next row from the supplied data, either directly from the array... + if ([self xmlDataArray]) { + xmlRow = NSArrayObjectAtIndex([self xmlDataArray], currentRowIndex); + } + // Or by reading an appropriate row from the streaming result else { - totalRows = [[self xmlDataArray] count]; - } - - // Set up an array of encoded field names as opening and closing tags - xmlRow = ([self xmlDataArray]) ? [[self xmlDataArray] objectAtIndex:0] : [streamingResult fetchFieldNames]; - - for (i = 0; i < [xmlRow count]; i++) - { - [xmlTags addObject:[NSMutableArray array]]; + xmlRow = [streamingResult fetchNextRowAsArray]; - [[xmlTags objectAtIndex:i] addObject:[NSString stringWithFormat:@"\t\t<%@>", [[[xmlRow objectAtIndex:i] description] HTMLEscapeString]]]; - [[xmlTags objectAtIndex:i] addObject:[NSString stringWithFormat:@"</%@>\n", [[[xmlRow objectAtIndex:i] description] HTMLEscapeString]]]; + if (!xmlRow) break; } - [[self exportOutputFileHandle] writeData:[xmlString dataUsingEncoding:[self exportOutputEncoding]]]; - - // Write an opening tag in the form of the table name - [[self exportOutputFileHandle] writeData:[[NSString stringWithFormat:@"\t<%@>\n", ([self xmlTableName]) ? [[self xmlTableName] HTMLEscapeString] : @"custom"] dataUsingEncoding:[self exportOutputEncoding]]]; + // Get the cell count if we don't already have it stored + if (!xmlRowCount) xmlRowCount = [xmlRow count]; - // Set up the starting row, which is 0 for streaming result sets and - // 1 for supplied arrays which include the column headers as the first row. - currentRowIndex = 0; + // Construct the row + [xmlString setString:@"\t<row>\n"]; - if ([self xmlDataArray]) currentRowIndex++; - - // Drop into the processing loop - NSAutoreleasePool *xmlExportPool = [[NSAutoreleasePool alloc] init]; - - currentPoolDataLength = 0; - - while (1) + for (i = 0; i < xmlRowCount; i++) { // Check for cancellation flag if ([self isCancelled]) { @@ -139,109 +170,75 @@ return; } - // Retrieve the next row from the supplied data, either directly from the array... - if ([self xmlDataArray]) { - xmlRow = NSArrayObjectAtIndex([self xmlDataArray], currentRowIndex); - } - // Or by reading an appropriate row from the streaming result - else { - xmlRow = [streamingResult fetchNextRowAsArray]; - - if (!xmlRow) break; - } - - // Get the cell count if we don't already have it stored - if (!xmlRowCount) xmlRowCount = [xmlRow count]; - - // Construct the row - [xmlString setString:@"\t<row>\n"]; - - for (i = 0; i < xmlRowCount; i++) - { - // Check for cancellation flag - if ([self isCancelled]) { - if (streamingResult) { - [connection cancelCurrentQuery]; - [streamingResult cancelResultLoad]; - } - - [xmlExportPool release]; - [pool release]; - - return; - } + // Retrieve the contents of this tag + if ([NSArrayObjectAtIndex(xmlRow, i) isKindOfClass:[NSData class]]) { + dataConversionString = [[NSString alloc] initWithData:NSArrayObjectAtIndex(xmlRow, i) encoding:[self exportOutputEncoding]]; - // Retrieve the contents of this tag - if ([NSArrayObjectAtIndex(xmlRow, i) isKindOfClass:[NSData class]]) { - dataConversionString = [[NSString alloc] initWithData:NSArrayObjectAtIndex(xmlRow, i) encoding:[self exportOutputEncoding]]; - - if (dataConversionString == nil) { - dataConversionString = [[NSString alloc] initWithData:NSArrayObjectAtIndex(xmlRow, i) encoding:NSASCIIStringEncoding]; - } - - [xmlItem setString:[NSString stringWithString:dataConversionString]]; - [dataConversionString release]; - } - else { - [xmlItem setString:[NSArrayObjectAtIndex(xmlRow, i) description]]; + if (dataConversionString == nil) { + dataConversionString = [[NSString alloc] initWithData:NSArrayObjectAtIndex(xmlRow, i) encoding:NSASCIIStringEncoding]; } - // Add the opening and closing tag and the contents to the XML string - [xmlString appendString:NSArrayObjectAtIndex(NSArrayObjectAtIndex(xmlTags, i), 0)]; - [xmlString appendString:[xmlItem HTMLEscapeString]]; - [xmlString appendString:NSArrayObjectAtIndex(NSArrayObjectAtIndex(xmlTags, i), 1)]; - } - - [xmlString appendString:@"\t</row>\n"]; - - // Record the total length for use with pool flushing - currentPoolDataLength += [xmlString length]; - - // Write the row to the filehandle - [[self exportOutputFileHandle] writeData:[xmlString dataUsingEncoding:[self exportOutputEncoding]]]; - - // Update the progress counter and progress bar - currentRowIndex++; - - // Update the progress - if (totalRows && (currentRowIndex * ([self exportMaxProgress] / totalRows)) > lastProgressValue) { - - NSInteger progress = (currentRowIndex * ([self exportMaxProgress] / totalRows)); - - [self setExportProgressValue:progress]; - - lastProgressValue = progress; - } - - // Inform the delegate that the export's progress has been updated - [delegate performSelectorOnMainThread:@selector(xmlExportProcessProgressUpdated:) withObject:self waitUntilDone:NO]; - - // Drain the autorelease pool as required to keep memory usage low - if (currentPoolDataLength > 250000) { - [xmlExportPool release]; - xmlExportPool = [[NSAutoreleasePool alloc] init]; + [xmlItem setString:[NSString stringWithString:dataConversionString]]; + [dataConversionString release]; + } + else { + [xmlItem setString:[NSArrayObjectAtIndex(xmlRow, i) description]]; } - // If an array was supplied and we've processed all rows, break - if ([self xmlDataArray] && totalRows == currentRowIndex) break; + // Add the opening and closing tag and the contents to the XML string + [xmlString appendString:NSArrayObjectAtIndex(NSArrayObjectAtIndex(xmlTags, i), 0)]; + [xmlString appendString:[xmlItem HTMLEscapeString]]; + [xmlString appendString:NSArrayObjectAtIndex(NSArrayObjectAtIndex(xmlTags, i), 1)]; } - // Write the closing tag for the table - [[self exportOutputFileHandle] writeData:[[NSString stringWithFormat:@"\t</%@>\n\n", ([self xmlTableName]) ? [[self xmlTableName] HTMLEscapeString] : @"custom"] dataUsingEncoding:[self exportOutputEncoding]]]; + [xmlString appendString:@"\t</row>\n"]; - // Write data to disk - [[self exportOutputFileHandle] synchronizeFile]; + // Record the total length for use with pool flushing + currentPoolDataLength += [xmlString length]; - // Mark the process as not running - [self setExportProcessIsRunning:NO]; + // Write the row to the filehandle + [[self exportOutputFileHandle] writeData:[xmlString dataUsingEncoding:[self exportOutputEncoding]]]; - // Inform the delegate that the export process is complete - [delegate performSelectorOnMainThread:@selector(xmlExportProcessComplete:) withObject:self waitUntilDone:NO]; + // Update the progress counter and progress bar + currentRowIndex++; - [xmlExportPool release]; - [pool release]; + // Update the progress + if (totalRows && (currentRowIndex * ([self exportMaxProgress] / totalRows)) > lastProgressValue) { + + NSInteger progress = (currentRowIndex * ([self exportMaxProgress] / totalRows)); + + [self setExportProgressValue:progress]; + + lastProgressValue = progress; + } + + // Inform the delegate that the export's progress has been updated + [delegate performSelectorOnMainThread:@selector(xmlExportProcessProgressUpdated:) withObject:self waitUntilDone:NO]; + + // Drain the autorelease pool as required to keep memory usage low + if (currentPoolDataLength > 250000) { + [xmlExportPool release]; + xmlExportPool = [[NSAutoreleasePool alloc] init]; + } + + // If an array was supplied and we've processed all rows, break + if ([self xmlDataArray] && totalRows == currentRowIndex) break; } - @catch (NSException *e) { } + + // Write the closing tag for the table + [[self exportOutputFileHandle] writeData:[[NSString stringWithFormat:@"\t</%@>\n\n", ([self xmlTableName]) ? [[self xmlTableName] HTMLEscapeString] : @"custom"] dataUsingEncoding:[self exportOutputEncoding]]]; + + // Write data to disk + [[self exportOutputFileHandle] synchronizeFile]; + + // Mark the process as not running + [self setExportProcessIsRunning:NO]; + + // Inform the delegate that the export process is complete + [delegate performSelectorOnMainThread:@selector(xmlExportProcessComplete:) withObject:self waitUntilDone:NO]; + + [xmlExportPool release]; + [pool release]; } /** |