GVZGraph.m 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*************************************************************************
  2. * Copyright (c) 2011 AT&T Intellectual Property
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * https://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors: Details at http://www.graphviz.org/
  9. *************************************************************************/
  10. #import "GVZGraph.h"
  11. #import "GVGraphArguments.h"
  12. #import "GVGraphDefaultAttributes.h"
  13. NSString *const GVGraphvizErrorDomain = @"GVGraphvizErrorDomain";
  14. extern double PSinputscale;
  15. static GVC_t *_graphContext = nil;
  16. @implementation GVZGraph
  17. @synthesize graph = _graph;
  18. @synthesize arguments = _arguments;
  19. @synthesize graphAttributes = _graphAttributes;
  20. @synthesize defaultNodeAttributes = _defaultNodeAttributes;
  21. @synthesize defaultEdgeAttributes = _defaultEdgeAttributes;
  22. extern char *gvplugin_list(GVC_t * gvc, api_t api, const char *str);
  23. + (void)initialize
  24. {
  25. _graphContext = gvContext();
  26. }
  27. + (NSArray *)pluginsWithAPI:(api_t)api
  28. {
  29. NSMutableSet *plugins = [NSMutableSet set];
  30. /* go through each non-empty plugin in the list, ignoring the package part */
  31. char *pluginList = gvplugin_list(_graphContext, api, ":");
  32. char *restOfPlugins;
  33. char *nextPlugin;
  34. for (restOfPlugins = pluginList; (nextPlugin = strsep(&restOfPlugins, " "));) {
  35. if (*nextPlugin) {
  36. char *lastColon = strrchr(nextPlugin, ':');
  37. if (lastColon) {
  38. *lastColon = '\0';
  39. [plugins addObject:[NSString stringWithCString:nextPlugin encoding:NSUTF8StringEncoding]];
  40. }
  41. }
  42. }
  43. free(pluginList);
  44. return [[plugins allObjects] sortedArrayUsingSelector:@selector(compare:)];
  45. }
  46. - (id)initWithURL:(NSURL *)URL error:(NSError **)outError
  47. {
  48. char *parentDir,*ptr;
  49. if (self = [super init]) {
  50. if ([URL isFileURL]) {
  51. /* open a FILE* on the file URL */
  52. FILE *file = fopen([[URL path] fileSystemRepresentation], "r");
  53. if (!file) {
  54. if (outError)
  55. *outError = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
  56. [self autorelease];
  57. return nil;
  58. }
  59. _graph = agread(file,0);
  60. if (!_graph) {
  61. if (outError)
  62. *outError = [NSError errorWithDomain:GVGraphvizErrorDomain code:GVFileParseError userInfo:nil];
  63. [self autorelease];
  64. return nil;
  65. }
  66. if(!agget(_graph,"imagepath")){
  67. parentDir = (char *)[[URL path] fileSystemRepresentation];
  68. ptr = strrchr(parentDir,'/');
  69. *ptr = 0;
  70. agattr(_graph,AGRAPH,"imagepath",parentDir);
  71. }
  72. fclose(file);
  73. }
  74. else {
  75. /* read the URL into memory */
  76. NSMutableData *memory = [NSMutableData dataWithContentsOfURL:URL options:0 error:outError];
  77. if (!memory) {
  78. [self autorelease];
  79. return nil;
  80. }
  81. /* null terminate the data */
  82. char nullByte = '\0';
  83. [memory appendBytes:&nullByte length:1];
  84. _graph = agmemread((char*)[memory bytes]);
  85. if (!_graph) {
  86. if (outError)
  87. *outError = [NSError errorWithDomain:GVGraphvizErrorDomain code:GVFileParseError userInfo:nil];
  88. [self autorelease];
  89. return nil;
  90. }
  91. }
  92. _freeLastLayout = NO;
  93. _arguments = [[GVGraphArguments alloc] initWithGraph:self];
  94. _graphAttributes = [[GVGraphDefaultAttributes alloc] initWithGraph:self prototype:AGRAPH];
  95. _defaultNodeAttributes = [[GVGraphDefaultAttributes alloc] initWithGraph:self prototype:AGNODE];
  96. _defaultEdgeAttributes = [[GVGraphDefaultAttributes alloc] initWithGraph:self prototype:AGEDGE];
  97. }
  98. return self;
  99. }
  100. - (BOOL)writeToURL:(NSURL *)URL error:(NSError **)outError
  101. {
  102. if ([URL isFileURL]) {
  103. /* open a FILE* on the file URL */
  104. FILE *file = fopen([[URL path] fileSystemRepresentation], "w");
  105. if (!file) {
  106. if (outError)
  107. *outError = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
  108. return NO;
  109. }
  110. /* write it out */
  111. if (agwrite(_graph, file) != 0) {
  112. if (outError)
  113. *outError = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
  114. return NO;
  115. }
  116. fclose(file);
  117. return YES;
  118. }
  119. else
  120. /* can't write out to non-file URL */
  121. return NO;
  122. }
  123. - (void)noteChanged:(BOOL)relayout
  124. {
  125. /* if we need to layout, apply globals and then relayout */
  126. if (relayout) {
  127. NSString* layout = [_arguments objectForKey:@"layout"];
  128. if (layout) {
  129. if (_freeLastLayout)
  130. gvFreeLayout(_graphContext, _graph);
  131. /* apply scale */
  132. NSString* scale = [_arguments objectForKey:@"scale"];
  133. PSinputscale = scale ? [scale doubleValue] : 0.0;
  134. if (PSinputscale == 0.0)
  135. PSinputscale = 72.0;
  136. if (gvLayout(_graphContext, _graph, (char*)[layout UTF8String]) != 0)
  137. @throw [NSException exceptionWithName:@"GVException" reason:@"bad layout" userInfo:nil];
  138. _freeLastLayout = YES;
  139. }
  140. }
  141. [[NSNotificationCenter defaultCenter] postNotificationName: @"GVGraphDidChange" object:self];
  142. }
  143. - (NSData*)renderWithFormat:(NSString *)format
  144. {
  145. char *renderedData = NULL;
  146. unsigned int renderedLength = 0;
  147. if (gvRenderData(_graphContext, _graph, (char*)[format UTF8String], &renderedData, &renderedLength) != 0)
  148. @throw [NSException exceptionWithName:@"GVException" reason:@"bad render" userInfo:nil];
  149. return [NSData dataWithBytesNoCopy:renderedData length:renderedLength freeWhenDone:YES];
  150. }
  151. - (void)renderWithFormat:(NSString *)format toURL:(NSURL *)URL
  152. {
  153. if ([URL isFileURL]) {
  154. if (gvRenderFilename(_graphContext, _graph, (char*)[format UTF8String], (char*)[[URL path] UTF8String]) != 0)
  155. @throw [NSException exceptionWithName:@"GVException" reason:@"bad render" userInfo:nil];
  156. }
  157. else
  158. [[self renderWithFormat:format] writeToURL:URL atomically:NO];
  159. }
  160. - (void)dealloc
  161. {
  162. if (_graph)
  163. agclose(_graph);
  164. [_arguments release];
  165. [_graphAttributes release];
  166. [_defaultNodeAttributes release];
  167. [_defaultEdgeAttributes release];
  168. [super dealloc];
  169. }
  170. @end