From bbe0f861dd4e3ab99aa3d555d3fc5db5ee5ae39d Mon Sep 17 00:00:00 2001 From: stuconnolly Date: Mon, 24 May 2010 18:07:43 +0000 Subject: Merge export redesign branch back into trunk. Includes a completely redesign approach to all export data types based on the use of NSOperation subclasses. CSV, SQL, XML and dot export types are currently functional, while the source files for PDF and HTML export types exist they are to be implemented, but are currently hidden from the interface. Also includes the following: - Completely redesigned export interface. - The ability to customize CSV NULL values. - The ability to specify whether the UTF-8 BOM should be used in SQL dumps. - The ability to specify whether BLOB fields are output as hex or plain text during SQL dumps. Defaults to hex. - Exporting currently selected tables via the tables list context menu. Outstanding issues: - Not all progress indicators for all export types are functional (or functioning correctly). - A few issues related to the introduction of only exporting the content and create and drop syntax of specific tables during SQL dumps. Needs some serious testing and benchmarking to ensure it replicates the current export functionality. --- Source/SPDotExporter.m | 227 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 Source/SPDotExporter.m (limited to 'Source/SPDotExporter.m') diff --git a/Source/SPDotExporter.m b/Source/SPDotExporter.m new file mode 100644 index 00000000..cbb132d9 --- /dev/null +++ b/Source/SPDotExporter.m @@ -0,0 +1,227 @@ +// +// $Id$ +// +// SPDotExporter.m +// sequel-pro +// +// Created by Stuart Connolly (stuconnolly.com) on April 17, 2010 +// Copyright (c) 2009 Stuart Connolly. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at + +#import "SPDotExporter.h" +#import "SPConstants.h" +#import "SPFileHandle.h" +#import "SPArrayAdditions.h" +#import "SPTableData.h" +#import "SPExportUtilities.h" + +@implementation SPDotExporter + +@synthesize delegate; +@synthesize dotExportTables; +@synthesize dotExportCurrentTable; +@synthesize dotTableData; +@synthesize dotDatabaseHost; +@synthesize dotDatabaseName; +@synthesize dotDatabaseVersion; + +/** + * Initialise an instance of SPDotExporter using the supplied delegate. + */ +- (id)initWithDelegate:(NSObject *)exportDelegate +{ + if ((self = [super init])) { + SPExportDelegateConformsToProtocol(exportDelegate, @protocol(SPDotExporterProtocol)); + + [self setDelegate:exportDelegate]; + [self setDotExportCurrentTable:nil]; + } + + return self; +} + +/** + * Start the Dot schema export process. This method is automatically called when an instance of this class + * is placed on an NSOperationQueue. Do not call it directly as there is no manual multithreading. + */ +- (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)) { + [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"; + } + + [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\n"]; + [metaString appendString:[NSString stringWithFormat:@"\t\t\t\n", hdrColor, tableName]]; + + // Get the column info + NSArray *columnInfo = [tableInfo objectForKey:@"columns"]; + + for (NSInteger j = 0; j < [columnInfo count]; j++ ) + { + [metaString appendString:[NSString stringWithFormat:@"\t\t\t\n", [[columnInfo objectAtIndex:j] objectForKey:@"name"], [[columnInfo objectAtIndex:j] objectForKey:@"name"], [[columnInfo objectAtIndex:j] objectForKey:@"type"]]]; + } + + [metaString appendString:@"\t\t\t
%@
%@:%@
>\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]]; + } + } + + // 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) { } +} + +/** + * Dealloc + */ +- (void)dealloc +{ + delegate = nil; + + [dotExportTables release], dotExportTables = nil; + [dotExportCurrentTable release], dotExportCurrentTable = nil; + [dotTableData release], dotTableData = nil; + [dotDatabaseHost release], dotDatabaseHost = nil; + [dotDatabaseName release], dotDatabaseName = nil; + [dotDatabaseVersion release], dotDatabaseVersion = nil; + + [super dealloc]; +} + +@end -- cgit v1.2.3