// // TableDump.m // sequel-pro // // Created by lorenz textor (lorenz@textor.ch) on Wed May 01 2002. // Copyright (c) 2002-2003 Lorenz Textor. 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 <http://code.google.com/p/sequel-pro/> // Or mail to <lorenz@textor.ch> #import "TableDump.h" #import "TableDocument.h" #import "TablesList.h" #import "TableSource.h" #import "TableContent.h" #import "CustomQuery.h" @implementation TableDump //IBAction methods - (IBAction)reloadTables:(id)sender /* get the tables in db */ { CMMCPResult *queryResult; int i; //get tables [tables removeAllObjects]; queryResult = [mySQLConnection listTables]; for ( i = 0 ; i < [queryResult numOfRows] ; i++ ) { [queryResult dataSeek:i]; [tables addObject:[NSMutableArray arrayWithObjects: [NSNumber numberWithBool:YES], [[queryResult fetchRowAsArray] objectAtIndex:0], nil]]; } [exportDumpTableView reloadData]; [exportMultipleCSVTableView reloadData]; [exportMultipleXMLTableView reloadData]; /* //disable buttons if there are no tables in db (or no db is selected) if ( ![tables count] ) { [dumpButton setEnabled:NO]; } else { [dumpButton setEnabled:YES]; } if ( ![tableDocumentInstance database] ) { [readButton setEnabled:NO]; } else { [readButton setEnabled:YES]; } */ } - (IBAction)selectTables:(id)sender /* selects or deselects all tables */ { NSEnumerator *enumerator; id theObject; [self reloadTables:self]; enumerator = [tables objectEnumerator]; while ( (theObject = [enumerator nextObject]) ) { if ( [sender tag] ) { [theObject replaceObjectAtIndex:0 withObject:[NSNumber numberWithBool:YES]]; } else { [theObject replaceObjectAtIndex:0 withObject:[NSNumber numberWithBool:NO]]; } } [exportDumpTableView reloadData]; [exportMultipleCSVTableView reloadData]; [exportMultipleXMLTableView reloadData]; } - (IBAction)closeSheet:(id)sender /* ends the modal session */ { [NSApp stopModalWithCode:[sender tag]]; } - (IBAction)stepRow:(id)sender /* displays next/previous row in fieldMapping tableView */ { if ( [sender tag] == 0 ) { currentRow--; [self setupFieldMappingArray]; } else { currentRow++; [self setupFieldMappingArray]; } //enable/disable buttons if ( currentRow == 0 ) { [rowDownButton setEnabled:NO]; [rowUpButton setEnabled:YES]; } else if ( currentRow == ([importArray count]-1) ) { [rowDownButton setEnabled:YES]; [rowUpButton setEnabled:NO]; } else { [rowDownButton setEnabled:YES]; [rowUpButton setEnabled:YES]; } } //export methods - (void)exportFile:(int)tag /* invoked when user clicks on an export menuItem */ { NSString *file; NSString *contextInfo; NSSavePanel *savePanel = [NSSavePanel savePanel]; NSString *currentDate = [[NSDate date] descriptionWithCalendarFormat:@"%d.%m.%Y" timeZone:nil locale:nil]; switch ( tag ) { case 5: //export dump [self reloadTables:self]; file = [NSString stringWithFormat:@"%@_dump %@.sql", [tableDocumentInstance database], currentDate]; [savePanel setAccessoryView:exportDumpView]; contextInfo = @"exportDump"; break; case 6: //export table content as CSV file = [NSString stringWithFormat:@"%@.csv", [tableDocumentInstance table]]; [savePanel setAccessoryView:exportCSVView]; contextInfo = @"exportTableContentAsCSV"; break; case 7: //export table content as XML file = [NSString stringWithFormat:@"%@.xml", [tableDocumentInstance table]]; contextInfo = @"exportTableContentAsXML"; break; case 8: //export custom result as CSV file = @"customresult.csv"; [savePanel setAccessoryView:exportCSVView]; contextInfo = @"exportCustomResultAsCSV"; break; case 9: //export custom result as XML file = @"customresult.xml"; contextInfo = @"exportCustomResultAsXML"; break; case 10: //export multiple tables as CSV [self reloadTables:self]; file = [NSString stringWithFormat:@"%@.csv", [tableDocumentInstance database]]; [savePanel setAccessoryView:exportMultipleCSVView]; contextInfo = @"exportMultipleTablesAsCSV"; break; case 11: //export multiple tables as XML [self reloadTables:self]; file = [NSString stringWithFormat:@"%@.xml", [tableDocumentInstance database]]; [savePanel setAccessoryView:exportMultipleXMLView]; contextInfo = @"exportMultipleTablesAsXML"; break; default: NSLog(@"ERROR: unknown export item with tag %d", tag); return; break; } //open savePanel [savePanel beginSheetForDirectory:[prefs objectForKey:@"savePath"] file:file modalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; } - (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo /* saves the export file */ { NSString *testString = @""; NSError **errorStr; id fileContent; BOOL success; [sheet orderOut:self]; if ( returnCode != NSOKButton ) return; //save path to preferences [prefs setObject:[sheet directory] forKey:@"savePath"]; //error if file exists and is not writable if ( [[NSFileManager defaultManager] fileExistsAtPath:[sheet filename]] ) { if ( ![[NSFileManager defaultManager] isWritableFileAtPath:[sheet filename]] ) { NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Couldn't replace the file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be replaced")); return; } } else { //error if file cannot be written if ( ![testString writeToFile:[sheet filename] atomically:YES] ) { NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); return; } } if ( [contextInfo isEqualToString:@"exportDump"] ) { //export dump of selected database fileContent = [self dumpForSelectedTables]; } else if ( [contextInfo isEqualToString:@"exportCustomResultAsCSV"] ) { //export custom query result as csv fileContent = [self csvForArray:[customQueryInstance currentResult] useFirstLine:[exportFieldNamesSwitch state] terminatedBy:[exportFieldsTerminatedField stringValue] enclosedBy:[exportFieldsEnclosedField stringValue] escapedBy:[exportFieldsEscapedField stringValue] lineEnds:[exportLinesTerminatedField stringValue] silently:NO]; } else if ( [contextInfo isEqualToString:@"exportTableContentAsCSV"] ) { //export table content as csv fileContent = [self csvForArray:[tableContentInstance currentResult] useFirstLine:[exportFieldNamesSwitch state] terminatedBy:[exportFieldsTerminatedField stringValue] enclosedBy:[exportFieldsEnclosedField stringValue] escapedBy:[exportFieldsEscapedField stringValue] lineEnds:[exportLinesTerminatedField stringValue] silently:NO]; } else if ( [contextInfo isEqualToString:@"exportMultipleTablesAsCSV"] ) { //export multiple tables as CSV fileContent = [self stringForSelectedTablesWithType:@"csv"]; } else if ( [contextInfo isEqualToString:@"exportCustomResultAsXML"] ) { //export custom query result as XML fileContent = [self xmlForArray:[customQueryInstance currentResult] tableName:@"custom" withHeader:YES silently:NO]; } else if ( [contextInfo isEqualToString:@"exportTableContentAsXML"] ) { //export table content as XML fileContent = [self xmlForArray:[tableContentInstance currentResult] tableName:[tableDocumentInstance table] withHeader:YES silently:NO]; } else if ( [contextInfo isEqualToString:@"exportMultipleTablesAsXML"] ) { //export multiple tables as XML fileContent = [self stringForSelectedTablesWithType:@"xml"]; } else { //unknown operation NSLog(@"unknown operation %@", [contextInfo description]); fileContent = @""; } if ( [fileContent respondsToSelector:@selector(writeToFile:atomically:encoding:error:)] ) { // mac os 10.4 or later success = [fileContent writeToFile:[sheet filename] atomically:YES encoding:[CMMCPConnection encodingForMySQLEncoding:[[tableDocumentInstance encoding] cString]] error:errorStr]; } else { // mac os pre 10.4 success = [fileContent writeToFile:[sheet filename] atomically:YES]; } if ( !success ) { NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Couldn't write to file. Be sure that you have the necessary privileges.", @"message of panel when file cannot be written")); } } //import methods /* - (IBAction)openDump:(id)sender opens the NSOpenPanel { [[NSOpenPanel openPanel] beginSheetForDirectory:[prefs objectForKey:@"openPath"] file:nil types:nil modalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:nil]; } */ - (void)importFile:(int)tag /* invoked when user clicks on an export menuItem */ { NSString *contextInfo; NSOpenPanel *openPanel = [NSOpenPanel openPanel]; switch ( tag ) { case 1: //import dump contextInfo = @"importDump"; break; case 2: //import CSV file [openPanel setAccessoryView:importCSVView]; contextInfo = @"importCSVFile"; break; default: NSLog(@"ERROR: unknown import item with tag %d", tag); return; break; } //open savePanel [openPanel beginSheetForDirectory:[prefs objectForKey:@"openPath"] file:nil modalForWindow:tableWindow modalDelegate:self didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; } - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(NSString *)contextInfo /* reads mysql-dumpfile */ { NSString *dumpFile; NSError **errorStr; NSMutableString *errors = [NSMutableString string]; [sheet orderOut:self]; if ( returnCode != NSOKButton ) return; //save path to preferences [prefs setObject:[sheet directory] forKey:@"openPath"]; //load file into string if ( [NSString respondsToSelector:@selector(stringWithContentsOfFile:encoding:error:)] ) { // mac os 10.4 or later dumpFile = [NSString stringWithContentsOfFile:[sheet filename] encoding:[CMMCPConnection encodingForMySQLEncoding:[[tableDocumentInstance encoding] cString]] error:errorStr]; } else { // mac os pre 10.4 dumpFile = [NSString stringWithContentsOfFile:[sheet filename]]; } if ( !dumpFile ) { NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, NSLocalizedString(@"Couldn't open file. Be sure that the path is correct and that you have the necessary privileges.", @"message of panel when file cannot be opened")); return; } //reset interface [errorsView setString:@""]; [errorsView displayIfNeeded]; [singleProgressText setStringValue:NSLocalizedString(@"Reading...", @"text showing that app is reading dump")]; [singleProgressText displayIfNeeded]; [singleProgressBar setDoubleValue:0]; [singleProgressBar displayIfNeeded]; if ( [contextInfo isEqualToString:@"importDump"] ) { //import dump file NSArray *queries; int i; //open progress sheet [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; [singleProgressBar setIndeterminate:YES]; [singleProgressBar setUsesThreadedAnimation:YES]; [singleProgressBar startAnimation:self]; //get array with an object for each mysql-query // queries = [dumpFile componentsSeparatedByString:@";\n"]; queries = [self splitQueries:dumpFile]; [singleProgressBar stopAnimation:self]; [singleProgressBar setUsesThreadedAnimation:NO]; [singleProgressBar setIndeterminate:NO]; //perform all mysql-queries for ( i = 0 ; i < [queries count] ; i++ ) { [singleProgressBar setDoubleValue:((i+1)*100/[queries count])]; [singleProgressBar displayIfNeeded]; [mySQLConnection queryString:[queries objectAtIndex:i]]; if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] && ![[mySQLConnection getLastErrorMessage] isEqualToString:@"Query was empty"]) [errors appendString:[NSString stringWithFormat:NSLocalizedString(@"[ERROR in query %d] %@\n", @"error text when multiple custom query failed"), (i+1),[mySQLConnection getLastErrorMessage]]]; } //close progress sheet [NSApp endSheet:singleProgressSheet]; [singleProgressSheet orderOut:nil]; //display errors if ( [errors length] ) { [errorsView setString:errors]; [NSApp beginSheet:errorsSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; [NSApp runModalForWindow:errorsSheet]; [NSApp endSheet:errorsSheet]; [errorsSheet orderOut:nil]; } } else if ( [contextInfo isEqualToString:@"importCSVFile"] ) { //import csv file int code; NSPopUpButtonCell *buttonCell = [[NSPopUpButtonCell alloc] init]; //open progress sheet [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; [singleProgressBar setIndeterminate:YES]; [singleProgressBar setUsesThreadedAnimation:YES]; [singleProgressBar startAnimation:self]; //put file in array if ( importArray ) [importArray release]; importArray = [[self arrayForCSV:dumpFile terminatedBy:[importFieldsTerminatedField stringValue] enclosedBy:[importFieldsEnclosedField stringValue] escapedBy:[importFieldsEscapedField stringValue] lineEnds:[importLinesTerminatedField stringValue]] retain]; //close progress sheet [NSApp endSheet:singleProgressSheet]; [singleProgressSheet orderOut:nil]; [singleProgressBar stopAnimation:self]; [singleProgressBar setUsesThreadedAnimation:NO]; [singleProgressBar setIndeterminate:NO]; //show fieldMapping sheet //set up tableView currentRow = 0; fieldMappingArray = nil; [self setupFieldMappingArray]; [rowDownButton setEnabled:NO]; if ( [importArray count] > 1 ) { [rowUpButton setEnabled:YES]; } else { [rowUpButton setEnabled:NO]; } //set up tableView buttons [buttonCell setControlSize:NSSmallControlSize]; [buttonCell setFont:[NSFont labelFontOfSize:[NSFont smallSystemFontSize]]]; [buttonCell addItemWithTitle:NSLocalizedString(@"Do not import", @"text for csv import drop downs")]; [buttonCell addItemsWithTitles:[tableSourceInstance fieldNames]]; [[fieldMappingTableView tableColumnWithIdentifier:@"1"] setDataCell:buttonCell]; [NSApp beginSheet:fieldMappingSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; code = [NSApp runModalForWindow:fieldMappingSheet]; [NSApp endSheet:fieldMappingSheet]; [fieldMappingSheet orderOut:nil]; if ( code ) { //import array into db NSMutableString *fNames = [NSMutableString string]; NSMutableArray *fValuesIndexes = [NSMutableArray array]; NSMutableString *fValues = [NSMutableString string]; int i,j; //open progress sheet [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; //get fields to be imported for ( i = 0 ; i < [fieldMappingArray count] ; i++ ) { if ( [[[fieldMappingArray objectAtIndex:i] objectAtIndex:1] intValue] > 0 ) { //field marked for import if ( [fNames length] ) [fNames appendString:@","]; [fNames appendString:[NSString stringWithFormat:@"`%@`", [[tableSourceInstance fieldNames] objectAtIndex:([[[fieldMappingArray objectAtIndex:i] objectAtIndex:1] intValue]-1)]]]; [fValuesIndexes addObject:[NSNumber numberWithInt:i]]; } } //import array for ( i = 0 ; i < [importArray count] ; i++ ) { //show progress bar [singleProgressBar setDoubleValue:((i+1)*100/[importArray count])]; [singleProgressBar displayIfNeeded]; if ( ![importFieldNamesSwitch state] || (i != 0) ) { //put values in string [fValues setString:@""]; for ( j = 0 ; j < [fValuesIndexes count] ; j++ ) { if ( [fValues length] ) [fValues appendString:@","]; if ( [[[importArray objectAtIndex:i] objectAtIndex:[[fValuesIndexes objectAtIndex:j] intValue]] isMemberOfClass:[NSNull class]] ) { [fValues appendString:@"NULL"]; } else { [fValues appendString:[NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString: [[importArray objectAtIndex:i] objectAtIndex:[[fValuesIndexes objectAtIndex:j] intValue]]]]]; // [fValues appendString:[NSString stringWithFormat:@"\"%@\"", // [mySQLConnection prepareString: // [[importArray objectAtIndex:i] objectAtIndex:[[fValuesIndexes objectAtIndex:j] intValue]]]]]; } } //perform query [mySQLConnection queryString:[NSString stringWithFormat:@"INSERT INTO `%@` (%@) VALUES (%@)", [tablesListInstance table], fNames, fValues]]; if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) [errors appendString:[NSString stringWithFormat:NSLocalizedString(@"[ERROR in line %d] %@\n", @"error text when reading of csv file gave errors"), (i+1),[mySQLConnection getLastErrorMessage]]]; } } //close progress sheet [NSApp endSheet:singleProgressSheet]; [singleProgressSheet orderOut:nil]; } //display errors if ( [errors length] ) { [errorsView setString:errors]; [NSApp beginSheet:errorsSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; [NSApp runModalForWindow:errorsSheet]; [NSApp endSheet:errorsSheet]; [errorsSheet orderOut:nil]; } //free arrays fieldMappingArray = nil; importArray = nil; } } - (void)setupFieldMappingArray /* sets up the fieldMapping array to be shown in the tableView */ { int i, value; if ( fieldMappingArray ) { for ( i = 0 ; i < [fieldMappingArray count] ; i++ ) { if ( [[[importArray objectAtIndex:currentRow] objectAtIndex:i] isKindOfClass:[NSNull class]] ) { [[fieldMappingArray objectAtIndex:i] replaceObjectAtIndex:0 withObject:@"NULL"]; } else { [[fieldMappingArray objectAtIndex:i] replaceObjectAtIndex:0 withObject:[[importArray objectAtIndex:currentRow] objectAtIndex:i]]; } } } else { fieldMappingArray = [NSMutableArray array]; for ( i = 0 ; i < [[importArray objectAtIndex:currentRow] count] ; i++ ) { if ( i < [[tableSourceInstance fieldNames] count] ) { value = i + 1; } else { value = 0; } if ( [[[importArray objectAtIndex:currentRow] objectAtIndex:i] isKindOfClass:[NSNull class]] ) { [fieldMappingArray addObject:[NSMutableArray arrayWithObjects:@"NULL", [NSNumber numberWithInt:value], nil]]; } else { [fieldMappingArray addObject:[NSMutableArray arrayWithObjects:[[importArray objectAtIndex:currentRow] objectAtIndex:i], [NSNumber numberWithInt:value], nil]]; } } [fieldMappingArray retain]; } [fieldMappingTableView reloadData]; } //format methods - (NSString *)dumpForSelectedTables /* returns a dump string for the selected tables */ { int i,j,k,t,rowCount,tableCount; CMMCPResult *queryResult; NSString *tableName; NSArray *fieldNames; NSArray *theRow; NSMutableString *rowValue = [NSMutableString string]; NSMutableArray *fieldValues; NSMutableString *dump = [NSMutableString string]; NSMutableString *errors = [NSMutableString string]; id createTableSyntax; //reset interface [errorsView setString:@""]; [errorsView displayIfNeeded]; [singleProgressText setStringValue:NSLocalizedString(@"Dumping...", @"text showing that app is writing dump")]; [singleProgressText displayIfNeeded]; [singleProgressBar setDoubleValue:0]; [singleProgressBar displayIfNeeded]; //open progress sheet [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; //count tables tableCount = 0; for ( i = 0 ; i < [tables count] ; i++ ) { if ( [[[tables objectAtIndex:i] objectAtIndex:0] boolValue] ) { tableCount++; } } k = 0; //add header of dump-file // [dump appendString:[NSString stringWithFormat:@"# Tables dumped %@\n# Created by Sequel Pro (Copyright (c) 2002-2003 Lorenz Textor)\n#\n# Host: %@ Database: %@\n# ******************************\n\n", [NSDate date], [tableDocumentInstance host], [tableDocumentInstance database]]]; [dump appendString:@"# Sequel Pro dump\n"]; [dump appendString:[NSString stringWithFormat:@"# Version %@\n", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; [dump appendString:@"# http://code.google.com/p/sequel-pro\n#\n"]; [dump appendString:[NSString stringWithFormat:@"# Host: %@ (MySQL %@)\n", [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; [dump appendString:[NSString stringWithFormat:@"# Database: %@\n", [tableDocumentInstance database]]]; [dump appendString:[NSString stringWithFormat:@"# Generation Time: %@\n", [NSDate date]]]; [dump appendString:@"# ************************************************************\n\n"]; for ( i = 0 ; i < [tables count] ; i++ ) { if ( [[[tables objectAtIndex:i] objectAtIndex:0] boolValue] ) { k++; //set progressbar and text tableName = [[tables objectAtIndex:i] objectAtIndex:1]; [singleProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Dumping table %@...", @"text showing that app is dumping table"), tableName]]; [singleProgressText displayIfNeeded]; //add name of table [dump appendString:[NSString stringWithFormat:@"# Dump of table %@\n# ------------------------------------------------------------\n\n", tableName]]; //add drop table if ( [addDropTableSwitch state] == NSOnState ) [dump appendString:[NSString stringWithFormat:@"DROP TABLE IF EXISTS `%@`;\n\n", tableName]]; //add create syntax for table if ( [addCreateTableSwitch state] == NSOnState ) { queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE TABLE `%@`", tableName]]; if ( [queryResult numOfRows] ) { createTableSyntax = [[queryResult fetchRowAsDictionary] objectForKey:@"Create Table"]; if ( [createTableSyntax isKindOfClass:[NSData class]] ) { createTableSyntax = [[[NSString alloc] initWithData:createTableSyntax encoding:[mySQLConnection encoding]] autorelease]; } [dump appendString:[NSString stringWithFormat:@"%@;\n\n", createTableSyntax]]; } if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { [errors appendString:[NSString stringWithFormat:@"%@\n", [mySQLConnection getLastErrorMessage]]]; if ( [addErrorsSwitch state] == NSOnState ) { [dump appendString:[NSString stringWithFormat:@"# Error: %@\n", [mySQLConnection getLastErrorMessage]]]; } } } //add table content if ( [addTableContentSwitch state] == NSOnState ) { queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM `%@`", tableName]]; fieldNames = [queryResult fetchFieldNames]; rowCount = [queryResult numOfRows]; for ( j = 0 ; j < rowCount ; j++ ) { [queryResult dataSeek:j]; theRow = [queryResult fetchRowAsArray]; fieldValues = [NSMutableArray array]; for ( t = 0 ; t < [theRow count] ; t++ ) { if ( [[theRow objectAtIndex:t] isKindOfClass:[NSData class]] ) { //escape special characters [rowValue setString:[mySQLConnection prepareBinaryData:[theRow objectAtIndex:t]]]; } else { [rowValue setString:[[theRow objectAtIndex:t] description]]; //escape special characters [rowValue setString:[mySQLConnection prepareString:rowValue]]; } if ( [[theRow objectAtIndex:t] isMemberOfClass:[NSNull class]] ) { [fieldValues addObject:@"NULL"]; } else { // [fieldValues addObject:[NSString stringWithFormat:@"\"%@\"", rowValue]]; [fieldValues addObject:[NSString stringWithFormat:@"'%@'", rowValue]]; } } [dump appendString:[NSString stringWithFormat:@"INSERT INTO `%@` (`%@`) VALUES (%@);\n", tableName, [fieldNames componentsJoinedByString:@"`,`"], [fieldValues componentsJoinedByString:@","]]]; } if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { [errors appendString:[NSString stringWithFormat:@"%@\n", [mySQLConnection getLastErrorMessage]]]; if ( [addErrorsSwitch state] == NSOnState ) { [dump appendString:[NSString stringWithFormat:@"# Error: %@\n", [mySQLConnection getLastErrorMessage]]]; } } } //set progressbar and text [singleProgressBar setDoubleValue:(k*100/tableCount)]; [singleProgressBar displayIfNeeded]; [dump appendString:@"\n\n"]; } } //close progress sheet [NSApp endSheet:singleProgressSheet]; [singleProgressSheet orderOut:nil]; //show errors sheet if there have been errors if ( [errors length] ) { [errorsView setString:errors]; [NSApp beginSheet:errorsSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; [NSApp runModalForWindow:errorsSheet]; [NSApp endSheet:errorsSheet]; [errorsSheet orderOut:nil]; } return [NSString stringWithString:dump]; } - (NSString *)csvForArray:(NSArray *)array useFirstLine:(BOOL)firstLine terminatedBy:(NSString *)terminated enclosedBy:(NSString *)enclosed escapedBy:(NSString *)escaped lineEnds:(NSString *)lineEnds silently:(BOOL)silently; /* takes an array and returns it as a csv string */ { NSMutableString *string = [NSMutableString string]; NSMutableString *rowValue = [NSMutableString string]; NSMutableArray *tempRow = [NSMutableArray array]; NSMutableString *tempTerminated, *tempLineEnds; int i,j; //repare tabs and line ends tempTerminated = [NSMutableString stringWithString:terminated]; [tempTerminated replaceOccurrencesOfString:@"\\t" withString:@"\t" options:NSLiteralSearch range:NSMakeRange(0, [tempTerminated length])]; [tempTerminated replaceOccurrencesOfString:@"\\n" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [tempTerminated length])]; [tempTerminated replaceOccurrencesOfString:@"\\r" withString:@"\r" options:NSLiteralSearch range:NSMakeRange(0, [tempTerminated length])]; terminated = [NSString stringWithString:tempTerminated]; tempLineEnds = [NSMutableString stringWithString:lineEnds]; [tempLineEnds replaceOccurrencesOfString:@"\\t" withString:@"\t" options:NSLiteralSearch range:NSMakeRange(0, [tempLineEnds length])]; [tempLineEnds replaceOccurrencesOfString:@"\\n" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [tempLineEnds length])]; [tempLineEnds replaceOccurrencesOfString:@"\\r" withString:@"\r" options:NSLiteralSearch range:NSMakeRange(0, [tempLineEnds length])]; lineEnds = [NSString stringWithString:tempLineEnds]; if ( !silently ) { //reset interface [singleProgressText setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")]; [singleProgressText displayIfNeeded]; [singleProgressBar setDoubleValue:0]; [singleProgressBar displayIfNeeded]; //open progress sheet [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; } for ( i = 0 ; i < [array count] ; i++ ) { if ( !silently ) { // [singleProgressText setStringValue:[NSString stringWithFormat:@"Writing row %d of %d", i+1, [array count]]]; // [singleProgressText displayIfNeeded]; [singleProgressBar setDoubleValue:((i+1)*100/[array count])]; [singleProgressBar displayIfNeeded]; } if ( (i > 0) || ((i == 0) && firstLine) ) { [tempRow removeAllObjects]; for ( j = 0 ; j < [[array objectAtIndex:i] count] ; j++ ) { //escape "enclosed by" character /* [rowValue setString:@""]; scanner = [NSScanner scannerWithString:[[[array objectAtIndex:i] objectAtIndex:j] description]]; [scanner setCharactersToBeSkipped:nil]; while ( ![scanner isAtEnd] ) { if ( [scanner scanUpToString:enclosed intoString:&tempString] ) { [rowValue appendString:tempString]; } if ( [scanner scanString:enclosed intoString:nil] ) { [rowValue appendString:[NSString stringWithFormat:@"%@%@", escaped, enclosed]]; } } */ [rowValue setString:[[[array objectAtIndex:i] objectAtIndex:j] description]]; if ( [rowValue isEqualToString:[prefs objectForKey:@"nullValue"]] ) { [tempRow addObject:@"NULL"]; } else { [rowValue replaceOccurrencesOfString:escaped withString:[NSString stringWithFormat:@"%@%@", escaped, escaped] options:NSLiteralSearch range:NSMakeRange(0,[rowValue length])]; if ( ![escaped isEqualToString:enclosed] ) { [rowValue replaceOccurrencesOfString:enclosed withString:[NSString stringWithFormat:@"%@%@", escaped, enclosed] options:NSLiteralSearch range:NSMakeRange(0,[rowValue length])]; } [rowValue replaceOccurrencesOfString:lineEnds withString:[NSString stringWithFormat:@"%@%@", escaped, lineEnds] options:NSLiteralSearch range:NSMakeRange(0,[rowValue length])]; if ( [enclosed isEqualToString:@""] ) { [rowValue replaceOccurrencesOfString:terminated withString:[NSString stringWithFormat:@"%@%@", escaped, terminated] options:NSLiteralSearch range:NSMakeRange(0,[rowValue length])]; } [tempRow addObject:[NSString stringWithFormat:@"%@%@%@", enclosed, rowValue, enclosed]]; } } [string appendString:[tempRow componentsJoinedByString:terminated]]; [string appendString:lineEnds]; } } /* //remove last line end [string deleteCharactersInRange:NSMakeRange(([string length]-[lineEnds length]),([lineEnds length]))]; */ if ( !silently ) { //close progress sheet [NSApp endSheet:singleProgressSheet]; [singleProgressSheet orderOut:nil]; } return [NSString stringWithString:string]; } - (NSArray *)arrayForCSV:(NSString *)csv terminatedBy:(NSString *)terminated enclosedBy:(NSString *)enclosed escapedBy:(NSString *)escaped lineEnds:(NSString *)lineEnds /* loads a csv string into an array */ { NSMutableString *tempTerminated, *tempLineEnds; NSMutableArray *tempArray = [NSMutableArray array]; NSMutableArray *tempRowArray = [NSMutableArray array]; NSMutableString *mutableField; NSScanner *scanner; NSString *scanString; NSMutableString *tempString = [NSMutableString string]; NSMutableArray *linesArray = [NSMutableArray array]; BOOL isEscaped, br; int fieldCount = nil; int x,i,j; //repare tabs and line ends tempTerminated = [NSMutableString stringWithString:terminated]; [tempTerminated replaceOccurrencesOfString:@"\\t" withString:@"\t" options:NSLiteralSearch range:NSMakeRange(0, [tempTerminated length])]; [tempTerminated replaceOccurrencesOfString:@"\\n" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [tempTerminated length])]; [tempTerminated replaceOccurrencesOfString:@"\\r" withString:@"\r" options:NSLiteralSearch range:NSMakeRange(0, [tempTerminated length])]; terminated = [NSString stringWithString:tempTerminated]; tempLineEnds = [NSMutableString stringWithString:lineEnds]; [tempLineEnds replaceOccurrencesOfString:@"\\t" withString:@"\t" options:NSLiteralSearch range:NSMakeRange(0, [tempLineEnds length])]; [tempLineEnds replaceOccurrencesOfString:@"\\n" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [tempLineEnds length])]; [tempLineEnds replaceOccurrencesOfString:@"\\r" withString:@"\r" options:NSLiteralSearch range:NSMakeRange(0, [tempLineEnds length])]; lineEnds = [NSString stringWithString:tempLineEnds]; //array with one line per object scanner = [NSScanner scannerWithString:csv]; [scanner setCharactersToBeSkipped:nil]; while ( ![scanner isAtEnd] ) { [tempString setString:@""]; br = NO; while ( !br ) { scanString = @""; [scanner scanUpToString:lineEnds intoString:&scanString]; [tempString appendString:scanString]; [scanner scanString:lineEnds intoString:&scanString]; //test if lineEnds-character is escaped isEscaped = NO; j = 1; if ( ![escaped isEqualToString:enclosed] && ![escaped isEqualToString:@""] ) { while ( ((j*[escaped length])<=[tempString length]) && ([[tempString substringWithRange:NSMakeRange(([tempString length]-(j*[escaped length])),[escaped length])] isEqualToString:escaped]) ) { isEscaped = !isEscaped; j++; } } if ( !isEscaped || [scanner isAtEnd] ) { //end of row br = YES; } else { //lineEnds-character was escaped [tempString appendString:scanString]; } } //add line to array [linesArray addObject:[NSString stringWithString:tempString]]; } for ( x = 0 ; x < [linesArray count] ; x++ ) { //separate fields [tempRowArray removeAllObjects]; [tempRowArray addObjectsFromArray:[self arrayForString:[linesArray objectAtIndex:x] enclosed:enclosed escaped:escaped terminated:terminated]]; if ( x == 0 ) { fieldCount = [tempRowArray count]; } else { while ( [tempRowArray count] < fieldCount ) { [tempRowArray addObject:@"NULL"]; } } for ( i = 0 ; i < [tempRowArray count] ; i++ ) { if ( [[tempRowArray objectAtIndex:i] isEqualToString:@"NULL"] || [[tempRowArray objectAtIndex:i] isEqualToString:@"\\N"] || [[tempRowArray objectAtIndex:i] isEqualToString:[prefs objectForKey:@"nullValue"]] ) { //put nsnull object to array if field contains un-enclosed NULL string [tempRowArray replaceObjectAtIndex:i withObject:[NSNull null]]; } else { //strip enclosed and escaped characters mutableField = [NSMutableString stringWithString:[tempRowArray objectAtIndex:i]]; //strip enclosed characters if ( [mutableField length] >= (2*[enclosed length]) ) { if ( [[mutableField substringToIndex:[enclosed length]] isEqualToString:enclosed] ) { [mutableField deleteCharactersInRange:NSMakeRange(0,[enclosed length])]; } if ( [[mutableField substringFromIndex:([mutableField length]-[enclosed length])] isEqualToString:enclosed] ) { [mutableField deleteCharactersInRange:NSMakeRange(([mutableField length]-[enclosed length]),[enclosed length])]; } } //strip escaped characters if ( ![enclosed isEqualToString:@""] ) { [mutableField replaceOccurrencesOfString:[NSString stringWithFormat:@"%@%@", escaped, enclosed] withString:enclosed options:NSLiteralSearch range:NSMakeRange(0, [mutableField length])]; } else { [mutableField replaceOccurrencesOfString:[NSString stringWithFormat:@"%@%@", escaped, terminated] withString:terminated options:NSLiteralSearch range:NSMakeRange(0, [mutableField length])]; } if ( ![lineEnds isEqualToString:@""] ) { [mutableField replaceOccurrencesOfString:[NSString stringWithFormat:@"%@%@", escaped, lineEnds] withString:lineEnds options:NSLiteralSearch range:NSMakeRange(0, [mutableField length])]; } if ( ![escaped isEqualToString:@""] && ![escaped isEqualToString:enclosed] ) { [mutableField replaceOccurrencesOfString:[NSString stringWithFormat:@"%@%@", escaped, escaped] withString:escaped options:NSLiteralSearch range:NSMakeRange(0, [mutableField length])]; } //add field to tempRowArray [tempRowArray replaceObjectAtIndex:i withObject:[NSString stringWithString:mutableField]]; } } //add row to tempArray [tempArray addObject:[NSArray arrayWithArray:tempRowArray]]; } return [NSArray arrayWithArray:tempArray]; } - (NSString *)xmlForArray:(NSArray *)array tableName:(NSString *)table withHeader:(BOOL)header silently:(BOOL)silently /* takes an array and returns it as a xml string */ { NSMutableString *string = [NSMutableString string]; int i,j; if ( !silently ) { //reset interface [singleProgressText setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")]; [singleProgressText displayIfNeeded]; [singleProgressBar setDoubleValue:0]; [singleProgressBar displayIfNeeded]; //open progress sheet [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; } if ( header ) { //add header [string appendString:@"<?xml version=\"1.0\"?>\n\n"]; [string appendString:@"<!--\n-\n"]; [string appendString:@"- Sequel Pro dump\n"]; [string appendString:[NSString stringWithFormat:@"- Version %@\n", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; [string appendString:@"- http://code.google.com/p/sequel-pro\n-\n"]; [string appendString:[NSString stringWithFormat:@"- Host: %@ (MySQL %@)\n", [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; [string appendString:[NSString stringWithFormat:@"- Database: %@\n", [tableDocumentInstance database]]]; [string appendString:[NSString stringWithFormat:@"- Generation Time: %@\n", [NSDate date]]]; [string appendString:@"-\n-->\n\n"]; } //add table name [string appendString:[NSString stringWithFormat:@"\t<%@>\n", [self htmlEscapeString:table]]]; for ( i = 1 ; i < [array count] ; i++ ) { if ( !silently ) { [singleProgressBar setDoubleValue:((i+1)*100/[array count])]; [singleProgressBar displayIfNeeded]; } //add rows [string appendString:@"\t<row>\n"]; for ( j = 0 ; j < [[array objectAtIndex:i] count] ; j++ ) { [string appendString:[NSString stringWithFormat:@"\t\t<%@>%@</%@>\n", [self htmlEscapeString:[[[array objectAtIndex:0] objectAtIndex:j] description]], [self htmlEscapeString:[[[array objectAtIndex:i] objectAtIndex:j] description]], [self htmlEscapeString:[[[array objectAtIndex:0] objectAtIndex:j] description]]]]; } [string appendString:@"\t</row>\n"]; } //end table name [string appendString:[NSString stringWithFormat:@"\t</%@>", [self htmlEscapeString:table]]]; if ( !silently ) { //close progress sheet [NSApp endSheet:singleProgressSheet]; [singleProgressSheet orderOut:nil]; } return [NSString stringWithString:string]; } - (NSString *)stringForSelectedTablesWithType:(NSString *)type /* returns a csv/xml string for the selected tables type has to be "csv" or "xml" */ { int i,j,k,t,rowCount,tableCount; CMMCPResult *queryResult; NSString *tableName; NSArray *fieldNames; NSArray *theRow; NSMutableArray *tableArray = [NSMutableArray array]; NSMutableString *rowValue = [NSMutableString string]; NSMutableArray *fieldValues; NSMutableString *dump = [NSMutableString string]; NSMutableString *errors = [NSMutableString string]; //reset interface [errorsView setString:@""]; [errorsView displayIfNeeded]; [singleProgressText setStringValue:NSLocalizedString(@"Writing...", @"text showing that app is writing text file")]; [singleProgressText displayIfNeeded]; [singleProgressBar setDoubleValue:0]; [singleProgressBar displayIfNeeded]; //open progress sheet [NSApp beginSheet:singleProgressSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; //count tables tableCount = 0; for ( i = 0 ; i < [tables count] ; i++ ) { if ( [[[tables objectAtIndex:i] objectAtIndex:0] boolValue] ) { tableCount++; } } k = 0; //add header of dump-file if ( [type isEqualToString:@"csv"] ) { [dump appendString:[NSString stringWithFormat:@"Host: %@ Database: %@ Generation Time: %@\n\n", [tableDocumentInstance host], [tableDocumentInstance database], [NSDate date]]]; } else if ( [type isEqualToString:@"xml"] ) { [dump appendString:@"<?xml version=\"1.0\"?>\n\n"]; [dump appendString:@"<!--\n-\n"]; [dump appendString:@"- Sequel Pro dump\n"]; [dump appendString:[NSString stringWithFormat:@"- Version %@\n", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]]]; [dump appendString:@"- http://code.google.com/p/sequel-pro\n-\n"]; [dump appendString:[NSString stringWithFormat:@"- Host: %@ (MySQL %@)\n", [tableDocumentInstance host], [tableDocumentInstance mySQLVersion]]]; [dump appendString:[NSString stringWithFormat:@"- Database: %@\n", [tableDocumentInstance database]]]; [dump appendString:[NSString stringWithFormat:@"- Generation Time: %@\n", [NSDate date]]]; [dump appendString:@"-\n-->\n\n\n"]; [dump appendString:[NSString stringWithFormat:@"<%@>\n\n\n", [self htmlEscapeString:[tableDocumentInstance database]]]]; } for ( i = 0 ; i < [tables count] ; i++ ) { if ( [[[tables objectAtIndex:i] objectAtIndex:0] boolValue] ) { k++; //set progressbar and text tableName = [[tables objectAtIndex:i] objectAtIndex:1]; [singleProgressText setStringValue:[NSString stringWithFormat:NSLocalizedString(@"Writing table %@...", @"text showing that app is writing table to text file"), tableName]]; [singleProgressText displayIfNeeded]; //add name of table if ( [type isEqualToString:@"csv"] ) { [dump appendString:[NSString stringWithFormat:@"Table %@\n\n", tableName]]; } //add table content queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SELECT * FROM `%@`", tableName]]; fieldNames = [queryResult fetchFieldNames]; rowCount = [queryResult numOfRows]; [tableArray removeAllObjects]; //add field names [tableArray addObject:fieldNames]; for ( j = 0 ; j < rowCount ; j++ ) { [queryResult dataSeek:j]; theRow = [queryResult fetchRowAsArray]; fieldValues = [NSMutableArray array]; for ( t = 0 ; t < [theRow count] ; t++ ) { if ( [[theRow objectAtIndex:t] isKindOfClass:[NSData class]] ) { //convert data to string [rowValue setString:[[NSString alloc] initWithData:[theRow objectAtIndex:t] encoding:[mySQLConnection encoding]]]; } else if ( [[theRow objectAtIndex:t] isMemberOfClass:[NSNull class]] ) { [rowValue setString:[prefs objectForKey:@"nullValue"]]; } else { [rowValue setString:[[theRow objectAtIndex:t] description]]; } [fieldValues addObject:[NSString stringWithString:rowValue]]; } [tableArray addObject:[NSArray arrayWithArray:fieldValues]]; } if ( ![[mySQLConnection getLastErrorMessage] isEqualToString:@""] ) { [errors appendString:[NSString stringWithFormat:@"%@\n", [mySQLConnection getLastErrorMessage]]]; } if ( [type isEqualToString:@"csv"] ) { [dump appendString:[self csvForArray:tableArray useFirstLine:[exportMultipleFieldNamesSwitch state] terminatedBy:[exportMultipleFieldsTerminatedField stringValue] enclosedBy:[exportMultipleFieldsEnclosedField stringValue] escapedBy:[exportMultipleFieldsEscapedField stringValue] lineEnds:[exportMultipleLinesTerminatedField stringValue] silently:YES]]; } else if ( [type isEqualToString:@"xml"] ) { [dump appendString:[self xmlForArray:tableArray tableName:tableName withHeader:NO silently:YES]]; } //set progressbar and text [singleProgressBar setDoubleValue:(k*100/tableCount)]; [singleProgressBar displayIfNeeded]; [dump appendString:@"\n\n\n"]; } } //write xml end if ( [type isEqualToString:@"xml"] ) { [dump appendString:[NSString stringWithFormat:@"</%@>", [self htmlEscapeString:[tableDocumentInstance database]]]]; } //close progress sheet [NSApp endSheet:singleProgressSheet]; [singleProgressSheet orderOut:nil]; //show errors sheet if there have been errors if ( [errors length] ) { [errorsView setString:errors]; [NSApp beginSheet:errorsSheet modalForWindow:tableWindow modalDelegate:self didEndSelector:nil contextInfo:nil]; [NSApp runModalForWindow:errorsSheet]; [NSApp endSheet:errorsSheet]; [errorsSheet orderOut:nil]; } return [NSString stringWithString:dump]; } - (NSString *)htmlEscapeString:(NSString *)string /* html escapes a string */ { NSMutableString *mutableString = [NSMutableString stringWithString:string]; [mutableString replaceOccurrencesOfString:@"&" withString:@"&" options:NSLiteralSearch range:NSMakeRange(0, [mutableString length])]; [mutableString replaceOccurrencesOfString:@"<" withString:@"<" options:NSLiteralSearch range:NSMakeRange(0, [mutableString length])]; [mutableString replaceOccurrencesOfString:@">" withString:@">" options:NSLiteralSearch range:NSMakeRange(0, [mutableString length])]; [mutableString replaceOccurrencesOfString:@"\"" withString:@""" options:NSLiteralSearch range:NSMakeRange(0, [mutableString length])]; return [NSString stringWithString:mutableString]; } - (NSArray *)arrayForString:(NSString *)string enclosed:(NSString *)enclosed escaped:(NSString *)escaped terminated:(NSString *)terminated /* split a string by the terminated-character if this is not escaped if enclosed-character is given, ignores characters inside enclosed-characters */ { NSMutableArray *tempArray = [NSMutableArray array]; BOOL inString = NO; BOOL isEscaped = NO; BOOL br = NO; unsigned i, j, start; char enc = nil; char esc = nil; char ter = nil; //we take only first character by now (too complicated otherwise) if ( [enclosed length] ) { enc = [enclosed characterAtIndex:0]; } if ( [escaped length] ) { esc = [escaped characterAtIndex:0]; } if ( [terminated length] ) { ter = [terminated characterAtIndex:0]; } start = 0; for ( i = 0 ; i < [string length] ; i++ ) { if ( inString ) { //we are in a string br = NO; while ( !br ) { if ( i >= [string length] ) { //end of string -> no second enclose character found br = YES; } else if ( [string characterAtIndex:i] == enc ) { //second enclose-character found //enclose-character escaped? isEscaped = NO; j = 1; while ( (i-j>0) && ([string characterAtIndex:(i-j)] == esc) ) { isEscaped = !isEscaped; j++; } if ( !isEscaped ) { inString = NO; br = YES; } } if ( !br ) i++; } } else if ( [string characterAtIndex:i] == ter ) { //terminated-character found if ( [enclosed isEqualToString:@""] ) { //check if terminated character is escaped isEscaped = NO; j = 1; while ( (i-j>0) && ([string characterAtIndex:(i-j)] == esc) ) { isEscaped = !isEscaped; j++; } if ( !isEscaped ) { [tempArray addObject:[string substringWithRange:NSMakeRange(start,(i-start))]]; start = i + 1; } } else { //add object to array //NSLog([string substringWithRange:NSMakeRange(start,(i-start))]); [tempArray addObject:[string substringWithRange:NSMakeRange(start,(i-start))]]; start = i + 1; } } else if ( [string characterAtIndex:i] == enc ) { //enclosed-character found inString = YES; } } //add rest of string to array [tempArray addObject:[string substringWithRange:NSMakeRange(start,([string length]-start))]]; return [NSArray arrayWithArray:tempArray]; } - (NSArray *)splitQueries:(NSString *)query /* splits the queries by ;'s which aren't inside any ", ' or ` characters */ { NSMutableString *queries = [NSMutableString stringWithString:query]; NSMutableArray *queryArray = [NSMutableArray array]; char stringType = nil; BOOL inString = NO; BOOL escaped; unsigned lineStart = 0; unsigned i, j, x, currentLineLength; //parse string for ( i = 0 ; i < [queries length] ; i++ ) { if ( inString ) { //we are in a string //look for end of string for ( ; i < [queries length] ; i++ ) { if ( (([queries characterAtIndex:i] == '`') && (stringType == '`')) || (([queries characterAtIndex:i] == stringType) && ([queries characterAtIndex:i-1] != '\\')) ) { //back-tick or no backslash before string end -> end of string inString = NO; break; } else if ( [queries characterAtIndex:i] == stringType ) { //check if string end isn't escaped escaped = YES; j = 2; currentLineLength = i - lineStart; while ( ((currentLineLength-j)>0) && ([queries characterAtIndex:i-j] == '\\') ) { escaped = !escaped; j++; } if ( !escaped ) { //it's really the end of the string inString = NO; } } } } else if ( ([queries characterAtIndex:i] == '#') || ((i+2<[queries length]) && ([queries characterAtIndex:i] == '-') && ([queries characterAtIndex:i+1] == '-') && ([queries characterAtIndex:i+2] == ' ')) ) { //it's a comment -> delete it x = i; while ( (x<[queries length]) && ([queries characterAtIndex:x] != '\r') && ([queries characterAtIndex:x] != '\n') ) { x++; } [queries deleteCharactersInRange:NSMakeRange(i,x-i)]; } else if ( [queries characterAtIndex:i] == ';' ) { //we are at the end of a query [queryArray addObject:[queries substringWithRange:NSMakeRange(lineStart, (i-lineStart))]]; while ( ((i+1)<[queries length]) && (([queries characterAtIndex:i+1]=='\n') || ([queries characterAtIndex:i+1]=='\r') || ([queries characterAtIndex:i+1]==' ')) ) { i++; } lineStart = i + 1; } else if ( ([queries characterAtIndex:i] == '\'') || ([queries characterAtIndex:i] == '"') || ([queries characterAtIndex:i] == '`') ) { //we are entering a string inString = YES; stringType = [queries characterAtIndex:i]; } } //add rest of string to array (if last line has not ended with a ";") if ( lineStart < [queries length] ) { [queryArray addObject:[queries substringWithRange:NSMakeRange(lineStart, ([queries length]-lineStart))]]; } //return array return [NSArray arrayWithArray:queryArray]; } //additional methods - (void)setConnection:(CMMCPConnection *)theConnection /* sets the connection (received from TableDocument) and makes things that have to be done only once */ { NSButtonCell *switchButton = [[NSButtonCell alloc] init]; prefs = [[NSUserDefaults standardUserDefaults] retain]; mySQLConnection = theConnection; //set up the interface [switchButton setButtonType:NSSwitchButton]; [switchButton setControlSize:NSSmallControlSize]; [[exportDumpTableView tableColumnWithIdentifier:@"switch"] setDataCell:switchButton]; [[exportMultipleCSVTableView tableColumnWithIdentifier:@"switch"] setDataCell:switchButton]; [[exportMultipleXMLTableView tableColumnWithIdentifier:@"switch"] setDataCell:switchButton]; if ( [prefs boolForKey:@"useMonospacedFonts"] ) { [[[exportDumpTableView tableColumnWithIdentifier:@"tables"] dataCell] setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; [[[exportMultipleCSVTableView tableColumnWithIdentifier:@"tables"] dataCell] setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; [[[exportMultipleXMLTableView tableColumnWithIdentifier:@"tables"] dataCell] setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; [[[fieldMappingTableView tableColumnWithIdentifier:@"0"] dataCell] setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; [errorsView setFont:[NSFont fontWithName:@"Monaco" size:[NSFont smallSystemFontSize]]]; } else { [[[exportDumpTableView tableColumnWithIdentifier:@"tables"] dataCell] setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; [[[exportMultipleCSVTableView tableColumnWithIdentifier:@"tables"] dataCell] setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; [[[exportMultipleXMLTableView tableColumnWithIdentifier:@"tables"] dataCell] setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; [[[fieldMappingTableView tableColumnWithIdentifier:@"0"] dataCell] setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; [errorsView setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; } // [self reloadTables:self]; } //tableView datasource methods - (int)numberOfRowsInTableView:(NSTableView *)aTableView; { if ( aTableView == fieldMappingTableView ) { return [fieldMappingArray count]; } else { return [tables count]; } } - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { if ( aTableView == fieldMappingTableView ) { return [[fieldMappingArray objectAtIndex:rowIndex] objectAtIndex:[[aTableColumn identifier] intValue]]; } else { if ( [[aTableColumn identifier] isEqualToString:@"switch"] ) { return [[tables objectAtIndex:rowIndex] objectAtIndex:0]; } else { return [[tables objectAtIndex:rowIndex] objectAtIndex:1]; } } } - (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { if ( aTableView == fieldMappingTableView ) { int i; for ( i = 0 ; i < [fieldMappingArray count] ; i++ ) { //check that field isn't already used if ( [[[fieldMappingArray objectAtIndex:i] objectAtIndex:1] isEqualToNumber:anObject] && (rowIndex != i) && ![anObject isEqualToNumber:[NSNumber numberWithInt:0]] ) { return; } } [[fieldMappingArray objectAtIndex:rowIndex] replaceObjectAtIndex:[[aTableColumn identifier] intValue] withObject:anObject]; } else { [[tables objectAtIndex:rowIndex] replaceObjectAtIndex:0 withObject:anObject]; } } //last but not least - (id)init; { self = [super init]; tables = [[NSMutableArray alloc] init]; return self; } - (void)dealloc { // NSLog(@"TableDump dealloc"); [tables release]; [importArray release]; [fieldMappingArray release]; [savePath release]; [openPath release]; [prefs release]; [super dealloc]; } @end