aboutsummaryrefslogtreecommitdiffstats
path: root/Source/SPDotExporter.m
diff options
context:
space:
mode:
Diffstat (limited to 'Source/SPDotExporter.m')
-rw-r--r--Source/SPDotExporter.m227
1 files changed, 227 insertions, 0 deletions
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 <http://code.google.com/p/sequel-pro/>
+
+#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<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 info
+ NSArray *columnInfo = [tableInfo objectForKey:@"columns"];
+
+ 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:@"\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]];
+ }
+ }
+
+ // 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