GVGraph.m 5.82 KB
Newer Older
1 2 3
/* $Id$ $Revision$ */
/* vim:set shiftwidth=4 ts=8: */

ellson's avatar
ellson committed
4 5 6 7 8 9 10 11 12
/*************************************************************************
 * Copyright (c) 2011 AT&T Intellectual Property 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: See CVS logs. Details at http://www.graphviz.org/
 *************************************************************************/
13

14

15 16 17 18
#import "GVGraph.h"
#import "GVGraphArguments.h"
#import "GVGraphDefaultAttributes.h"

19 20
NSString *const GVGraphvizErrorDomain = @"GVGraphvizErrorDomain";

21 22 23 24 25 26 27 28 29 30 31 32
extern double PSinputscale;

static GVC_t *_graphContext = nil;

@implementation GVGraph

@synthesize graph = _graph;
@synthesize arguments = _arguments;
@synthesize graphAttributes = _graphAttributes;
@synthesize defaultNodeAttributes = _defaultNodeAttributes;
@synthesize defaultEdgeAttributes = _defaultEdgeAttributes;

33 34
extern char *gvplugin_list(GVC_t * gvc, api_t api, const char *str);

35 36
+ (void)initialize
{
37
	_graphContext = gvContext();
38 39
}

40
+ (NSArray *)pluginsWithAPI:(api_t)api
41
{
42
	NSMutableSet *plugins = [NSMutableSet set];
43 44 45 46 47
	
	/* go through each non-empty plugin in the list, ignoring the package part */
	char *pluginList = gvplugin_list(_graphContext, api, ":");
	char *restOfPlugins;
	char *nextPlugin;
48
	for (restOfPlugins = pluginList; (nextPlugin = strsep(&restOfPlugins, " "));) {
49 50 51 52
		if (*nextPlugin) {
			char *lastColon = strrchr(nextPlugin, ':');
			if (lastColon) {
				*lastColon = '\0';
53
				[plugins addObject:[NSString stringWithCString:nextPlugin encoding:NSUTF8StringEncoding]];
54 55 56 57 58
			}
		}
	}
	free(pluginList);

59
	return [[plugins allObjects] sortedArrayUsingSelector:@selector(compare:)];
60 61 62
}

- (id)initWithURL:(NSURL *)URL error:(NSError **)outError
63
{
64
	char *parentDir,*ptr;
65 66 67 68 69 70 71 72 73 74 75
	if (self = [super init]) {
		if ([URL isFileURL]) {
			/* open a FILE* on the file URL */
			FILE *file = fopen([[URL path] fileSystemRepresentation], "r");
			if (!file) {
				if (outError)
					*outError = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
				[self autorelease];
				return nil;
			}
			
76
			_graph = agread(file,0);
77
			if (!_graph) {
78 79
				if (outError)
					*outError = [NSError errorWithDomain:GVGraphvizErrorDomain code:GVFileParseError userInfo:nil];
80 81 82
				[self autorelease];
				return nil;
			}
83 84 85 86
			if(!agget(_graph,"imagepath")){
					parentDir = (char *)[[URL path] fileSystemRepresentation];
					ptr = strrchr(parentDir,'/');
					*ptr = 0;
87
					agattr(_graph,AGRAPH,"imagepath",parentDir);
88
			}
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
			fclose(file);
		}
		else {
			/* read the URL into memory */
			NSMutableData *memory = [NSMutableData dataWithContentsOfURL:URL options:0 error:outError];
			if (!memory) {
				[self autorelease];
				return nil;
			}
			
			/* null terminate the data */
			char nullByte = '\0';
			[memory appendBytes:&nullByte length:1];
			
			_graph = agmemread((char*)[memory bytes]);
104 105 106 107 108 109
			if (!_graph) {
				if (outError)
					*outError = [NSError errorWithDomain:GVGraphvizErrorDomain code:GVFileParseError userInfo:nil];
				[self autorelease];
				return nil;
			}
110 111 112 113
		}

		_freeLastLayout = NO;
		_arguments = [[GVGraphArguments alloc] initWithGraph:self];
114 115 116
		_graphAttributes = [[GVGraphDefaultAttributes alloc] initWithGraph:self prototype:AGRAPH];
		_defaultNodeAttributes = [[GVGraphDefaultAttributes alloc] initWithGraph:self prototype:AGNODE];
		_defaultEdgeAttributes = [[GVGraphDefaultAttributes alloc] initWithGraph:self prototype:AGEDGE];
117 118 119 120 121
	}
	
	return self;
}

122
- (BOOL)writeToURL:(NSURL *)URL error:(NSError **)outError
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
{
	if ([URL isFileURL]) {
		/* open a FILE* on the file URL */
		FILE *file = fopen([[URL path] fileSystemRepresentation], "w");
		if (!file) {
			if (outError)
				*outError = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
			return NO;
		}
		
		/* write it out */
		if (agwrite(_graph, file) != 0) {
			if (outError)
				*outError = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
			return NO;
		}
		
		fclose(file);
		return YES;
	}
	else
		/* can't write out to non-file URL */
		return NO;
}

- (void)noteChanged:(BOOL)relayout
{
	/* if we need to layout, apply globals and then relayout */
	if (relayout) {
		NSString* layout = [_arguments objectForKey:@"layout"];
		if (layout) {
			if (_freeLastLayout)
				gvFreeLayout(_graphContext, _graph);
				
			/* apply scale */
			NSString* scale = [_arguments objectForKey:@"scale"];
			PSinputscale = scale ? [scale doubleValue] : 0.0;
			if (PSinputscale == 0.0)
				PSinputscale = 72.0;
		
			if (gvLayout(_graphContext, _graph, (char*)[layout UTF8String]) != 0)
				@throw [NSException exceptionWithName:@"GVException" reason:@"bad layout" userInfo:nil];
			_freeLastLayout = YES;
		}
	}
	

	[[NSNotificationCenter defaultCenter] postNotificationName: @"GVGraphDidChange" object:self];
}

173
- (NSData*)renderWithFormat:(NSString *)format
174 175 176 177 178 179 180 181 182
{
	char *renderedData = NULL;
	unsigned int renderedLength = 0;
	if (gvRenderData(_graphContext, _graph, (char*)[format UTF8String], &renderedData, &renderedLength) != 0)
		@throw [NSException exceptionWithName:@"GVException" reason:@"bad render" userInfo:nil];
	return [NSData dataWithBytesNoCopy:renderedData length:renderedLength freeWhenDone:YES];

}

183
- (void)renderWithFormat:(NSString *)format toURL:(NSURL *)URL
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
{
	if ([URL isFileURL]) {
		if (gvRenderFilename(_graphContext, _graph, (char*)[format UTF8String], (char*)[[URL path] UTF8String]) != 0)
			@throw [NSException exceptionWithName:@"GVException" reason:@"bad render" userInfo:nil];
	}
	else
		[[self renderWithFormat:format] writeToURL:URL atomically:NO];
}


- (void)dealloc
{
	if (_graph)
		agclose(_graph);
	
	[_arguments release];
	[_graphAttributes release];
	[_defaultNodeAttributes release];
	[_defaultEdgeAttributes release];
203
	[super dealloc];
204 205 206
}

@end