Second try (was Re: [GOODIE] MacOSXGraphVizGenerator-dd)

Lic. Edgar J. De Cleene edgardec2001 at yahoo.com.ar
Sat Mar 12 08:26:36 UTC 2005


On 11/03/05 19:47, "John Pierce" <john.raymond.pierce at gmail.com> wrote:

> I'm sorry if I misunderstand, but where is the tested Mac code for
> GraphViz I can incorporate?  I only got a morph attachment in the last
> email.
Yes, blame the 100 k limit.
Original mail have the two and was rejected, so I split
Here is yours original, more Dominique "idea", more my tune-up. (very
little)

>>Would you like developer access to my SqueakSource project so you can
>>just put in the Mac extensions?  That could be the most direct route
>>if you feel comfortable with that.

>>John

If you have a little time for IRC exchange , I prefer, but if not the case,
I agree.

I have a semi backed parse, and I hope finishing it today.
Some questions remains, as I load the png and other graphic formats
generated for GraphViz in image programs as GraphicConverter, and see the
pixels data don't match the inches data (in GraphicConverter and xxx.txt
exported files)

Also , when the png are very huge (more 10000 x 10000 pixels as originated
by Morphic-Basic) , don't be saved to disk,

I not familiar with GraphViz project, but like know if was possible do the
conversion on C side and have a new exporting format "Squeak compatible"
with more reliable/useful data.

Regards

Edgar

-------------- next part --------------
Object subclass: #GraphViz
	instanceVariableNames: 'graphName contentStream directed indentLevel'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GraphViz'!

!GraphViz methodsFor: 'accessing' stamp: 'jrp 2/26/2005 23:37'!
baseFileName

	^ graphName ifNil: ['unnamed']! !

!GraphViz methodsFor: 'accessing' stamp: 'jrp 2/26/2005 17:01'!
beDirected

	directed _ true! !

!GraphViz methodsFor: 'accessing' stamp: 'jrp 2/26/2005 17:03'!
beUndirected

	directed _ false! !

!GraphViz methodsFor: 'accessing' stamp: 'jrp 2/28/2005 23:39'!
dot

	^ String streamContents: [:stream |
		directed ifTrue: [stream nextPutAll: 'di'].
		stream nextPutAll: 'graph'.
		graphName ifNotNilDo: [:name | stream nextPut: $  ;nextPutAll: (self safeIdFor: name)].
		stream nextPutAll: ' {'.
		stream nextPutAll: contentStream contents.		
		stream cr; nextPut: $}]! !

!GraphViz methodsFor: 'accessing' stamp: 'jrp 2/27/2005 21:39'!
dotExtension

	^ '.dot'! !

!GraphViz methodsFor: 'accessing' stamp: 'jrp 2/27/2005 21:39'!
dotFileName

	^ self baseFileName, self dotExtension! !

!GraphViz methodsFor: 'accessing' stamp: 'jrp 2/26/2005 16:14'!
name

	^ graphName! !

!GraphViz methodsFor: 'accessing' stamp: 'jrp 2/26/2005 16:14'!
name: aString

	graphName _ aString! !

!GraphViz methodsFor: 'accessing' stamp: 'jrp 2/27/2005 23:55'!
safeIdFor: anObject

	| hasWhiteSpace firstCharIsDigit |
	anObject isNumber ifTrue: [^ anObject asString].
	anObject isString ifTrue:
		[(anObject first = $< and: [anObject last = $>]) ifTrue: [^ anObject].
		(anObject first = $" and: [anObject last = $"]) ifTrue: [^ anObject].
		
		hasWhiteSpace _ anObject lastSpacePosition > 0.
		hasWhiteSpace ifTrue: [^ '"', anObject, '"'].
		
		firstCharIsDigit _ anObject first isDigit.
		firstCharIsDigit ifTrue: [^ '"', anObject, '"'].

		anObject do: [:char | (char isAlphaNumeric or: [char = $_]) ifFalse: [^ '"', anObject, '"']]].
	
	^ anObject asString! !


!GraphViz methodsFor: 'graphing' stamp: 'jrp 2/27/2005 21:12'!
add: anObject

	self add: anObject with: #()! !

!GraphViz methodsFor: 'graphing' stamp: 'jrp 2/27/2005 21:30'!
add: anObject with: associations

	| attributes |
	
	anObject isVariableBinding
		ifFalse: [self writeNode: anObject]
		ifTrue: [self writeEdge: anObject].
	
	attributes _ associations isCollection ifTrue: [associations] ifFalse: [{associations}].
	attributes isEmpty 
		ifTrue: [contentStream nextPut: $;]
		ifFalse: [self writeAttributes: attributes]! !

!GraphViz methodsFor: 'graphing' stamp: 'jrp 2/28/2005 23:42'!
indentStream

	indentLevel timesRepeat: [contentStream tab]! !

!GraphViz methodsFor: 'graphing' stamp: 'jrp 3/1/2005 07:40'!
subgraph: aString do: aBlock

	contentStream cr.
	self indentStream.
	contentStream nextPutAll: 'subgraph'.
	aString ifNotNilDo: [:name | contentStream nextPut: $  ;nextPutAll: (self safeIdFor: name)].
	contentStream nextPutAll: ' {'.
	
	indentLevel _ indentLevel + 1.
	[aBlock value] ensure:
		[indentLevel _ indentLevel - 1.	
		contentStream cr.
		self indentStream.
		contentStream nextPut: $}]! !

!GraphViz methodsFor: 'graphing' stamp: 'jrp 3/1/2005 07:40'!
subgraphDo: aBlock

	self subgraph: nil do: aBlock! !

!GraphViz methodsFor: 'graphing' stamp: 'jrp 2/27/2005 23:43'!
writeAttributes: associations
	
	contentStream nextPutAll: ' ['.
	associations
		do: [:each | 
			contentStream
				nextPutAll: (self safeIdFor: each key);
				nextPut: $=;
				nextPutAll: (self safeIdFor: each value)]
		separatedBy: [contentStream nextPut: $,].
		
	contentStream nextPutAll: '];'! !

!GraphViz methodsFor: 'graphing' stamp: 'jrp 2/28/2005 23:42'!
writeEdge: anAssociation

	contentStream cr.
	self indentStream.
	contentStream nextPutAll: (self safeIdFor: anAssociation key).
	
	directed 
		ifTrue: [contentStream nextPutAll: ' -> ']
		ifFalse: [contentStream nextPutAll: ' -- '].
		
	contentStream nextPutAll: (self safeIdFor: anAssociation value)! !

!GraphViz methodsFor: 'graphing' stamp: 'jrp 2/28/2005 23:40'!
writeNode: anObject

	contentStream cr.
	self indentStream.
	contentStream nextPutAll: (self safeIdFor: anObject)! !

!GraphViz methodsFor: 'graphing' stamp: 'jrp 2/26/2005 23:36'!
writeToFile

	self writeToFileNamed: self dotFileName! !

!GraphViz methodsFor: 'graphing' stamp: 'jrp 2/27/2005 21:40'!
writeToFileNamed: aString

	| fileName file |
	fileName _ aString.
	(aString endsWith: self dotExtension) ifFalse: [fileName _ fileName, self dotExtension].
	
	file _ FileStream forceNewFileNamed: fileName.
	[file nextPutAll: self dot] ensure: [file close]! !


!GraphViz methodsFor: 'initialization' stamp: 'jrp 2/28/2005 23:41'!
initialize

	directed _ false.
	contentStream _ String new writeStream.
	indentLevel _ 1.! !


!GraphViz methodsFor: 'displaying' stamp: 'edc 3/11/2005 09:32'!
asMorph

	| generator maxTimesToWait try fondoA t aWindow title |
	self writeToFile.
	generator := self class defaultGeneratorClass new.
	directed
		ifTrue: [generator runDotOn: self outputFormat: #jpg]
		ifFalse: [generator runNeatoOn: self outputFormat: #jpg].
	
	maxTimesToWait := 22.
	try := 0.
	
	fondoA:= [(ImageReadWriter formFromFileNamed: self baseFileName, '.png') asMorph]
		on: Error 
		do: 
			[:err |
			try := try + 1.
			try > maxTimesToWait ifTrue: [^ 'GraphViz image could not be generated in approximately 45 seconds or less' asMorph]. 
			2 second asDelay wait. err retry].
		
	

	t := ScrollPane new.
	t scroller addMorph: fondoA.
	
	title := 'Squeak Road Map For : ' ,self baseFileName .
	aWindow := (SystemWindow labelled: title)
				model: nil.
	aWindow
		bounds: (fondoA position - (0 @ aWindow labelHeight + aWindow borderWidth) corner: fondoA bottomRight + aWindow borderWidth).
	aWindow
		addMorph: t
		frame: (0 @ 0 extent: 1 @ 1).
	aWindow setWindowColor: Color veryLightGray.
	^aWindow .! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

GraphViz class
	instanceVariableNames: ''!

!GraphViz class methodsFor: 'samples' stamp: 'jrp 3/2/2005 00:08'!
directedGraphSample1

	"GraphViz directedGraphSample1"
	| graph |
	graph _ GraphViz new.
	
	graph
		beDirected;
		name: #directedGraphSample1;
		add: #graph with: {#ratio -> '0.5'};
		add: #node with: {#fontsize -> 10};
		
		add: #Smalltalk -> #Squeak;
		add: #Smalltalk -> 'C++';
		add: 'C++' -> #Java;
		add: #Java -> 'C#';
		
		openAsMorph! !

!GraphViz class methodsFor: 'samples' stamp: 'jrp 3/1/2005 07:39'!
directedGraphSample2

	"GraphViz directedGraphSample2"
	| graph |
	graph _ GraphViz new.

	graph beDirected.
	graph
		name: #directedGraphSample2;
		add: #node with: {#fontsize -> 28};
		
		subgraph: #clusterfoo do:
			[graph
				add: #subgraph with: {#style -> #filled. #fillcolor -> #lightgrey};
				add: #A -> #B;
				add: #subgraph with: #label -> #Foo].
		
	graph
		add: #A -> #J;
		add: #B -> #Z;
		
		add: #graph with: {#label -> 'Cluster Graph'. #fontsize -> 50};
		
		openAsMorph! !

!GraphViz class methodsFor: 'samples' stamp: 'edc 3/10/2005 17:28'!
hierarchyForClassesInCategoriesMatching: symbol

	"GraphViz hierarchyForClassesInCategoriesMatching: 'System-Archives'"
	"GraphViz hierarchyForClassesInCategoriesMatching: 'Collections-Weak'"
	"GraphViz hierarchyForClassesInCategoriesMatching: #DynamicBindings"
			
	| graph edges |
	graph := GraphViz new.

	graph beDirected.
	graph
		name: symbol;
		add: #graph with: {#overlap -> #scale. #concentrate -> #true. #bgcolor -> #transparent};
		add: #node with: {#shape -> #box. #fontsize -> 11. #style -> #filled. #fillcolor -> #tomato};
		add: #edge with: {#arrowsize -> 0.5}.
		
	edges := Set new.
	(SystemOrganization categoriesMatching: (symbol, '*')) do:
		[:cat |
	
		(SystemOrganization listAtCategoryNamed: cat) do:
			[:klass | | browser hierarchy |
			graph add: klass with: {#fillcolor -> #palegreen}.
			
			browser := HierarchyBrowser new initHierarchyForClass: (Smalltalk at: klass).
			hierarchy := (browser classList collect: [:each | each withBlanksTrimmed asSymbol]).
			hierarchy pairsDo: [:first :second | edges add: first -> second]]].

		edges do: [:each | graph add: each].

	graph
		add: #graph with: {#label -> symbol. #fontsize -> 24};
		openAsMorph.
! !

!GraphViz class methodsFor: 'samples' stamp: 'jrp 3/3/2005 00:20'!
referencesToClassesInCategoriesMatching: symbol

	"GraphViz referencesToClassesInCategoriesMatching: 'System-Archives'"
	"GraphViz referencesToClassesInCategoriesMatching: 'Collections-Weak'"
	"GraphViz referencesToClassesInCategoriesMatching: #DynamicBindings"
			
	| graph edges |
	graph _ GraphViz new.

	graph
		name: symbol;
		add: #graph with: {#overlap -> #scale. #concentrate -> #true. #start -> #rand. #splines -> #true. #bgcolor -> #transparent};
		add: #node with: {#shape -> #box. #fontsize -> 14. #style -> #filled. #fillcolor -> #tomato};
		add: #edge with: {#arrowtail -> #normal. #arrowsize -> 0.5}.

	edges _ Set new.
	(SystemOrganization categoriesMatching: (symbol, '*')) do:
		[:cat |
	
		(SystemOrganization listAtCategoryNamed: cat) do:
			[:klass |
			graph add: klass with: {#fillcolor -> #palegreen}.
			
			(Smalltalk at: klass) allCallsOn do: [:each | edges add: klass -> each classSymbol]]].

		edges do: [:each | graph add: each].

	graph
		add: #graph with: {#label -> symbol. #fontsize -> 40};
		openAsMorph! !

!GraphViz class methodsFor: 'samples' stamp: 'jrp 2/28/2005 07:37'!
undirectedGraphSample1

	"GraphViz undirectedGraphSample1"
	| graph |
	graph _ GraphViz new.

	graph
		name: #undirectedGraphSample1;
		add: #graph with: #start -> #rand;
		add: #node with: {#fontsize -> 28};
		
		add: #A -> #X;
		
		add: #node with: #shape -> #box;
		
		add: #J with: {#style -> #filled. #fillcolor -> #red. #label -> 'J\nRed'};
		add: #A -> #J;
		add: #A -> #Z;
		
		add: #graph with: {#label -> 'Test Graph'. #fontsize -> 50};
		
		openAsMorph! !

!GraphViz class methodsFor: 'samples' stamp: 'jrp 3/2/2005 23:32'!
usersOfBag

	"GraphViz usersOfBag"
	| graph |
	graph _ GraphViz new.

	graph
		name: #usersOfBag;
		add: #graph with: {#size -> '8,8'. #overlap -> #scale. #splines -> #true. #concentrate -> #true};
		add: #Bag;
		add: #node with: {#fontsize -> 10}.
		
	Bag allCallsOn do: 
		[:each |
		graph add: (each classSymbol, '>>', each methodSymbol) with: #label -> (each classSymbol, '>>\n', each methodSymbol).
		graph 
			add: #Bag -> (each classSymbol, '>>', each methodSymbol)
			with: {#arrowtail -> #normal. #arrowsize -> 0.5}].

	graph
		add: #graph with: {#label -> 'Users of Bag'. #fontsize -> 50};
		
		openAsMorph! !

!GraphViz class methodsFor: 'samples' stamp: 'edc 3/10/2005 11:14'!
usersOfMorph

	"GraphViz usersOfMorph"
	| graph |
	graph := GraphViz new.

	graph
		name: #usersOfBag;
		add: #graph with: {#size -> '8,8'. #overlap -> #scale. #splines -> #true. #concentrate -> #true};
		add: #Morph;
		add: #node with: {#fontsize -> 10}.
		
	Morph allCallsOn do: 
		[:each |
		graph add: (each classSymbol, '>>', each methodSymbol) with: #label -> (each classSymbol, '>>\n', each methodSymbol).
		graph 
			add: #Morph -> (each classSymbol, '>>', each methodSymbol)
			with: {#arrowtail -> #normal. #arrowsize -> 0.5}].

	graph
		add: #graph with: {#label -> 'Users of Morph'. #fontsize -> 36};
		
		openAsMorph! !


!GraphViz class methodsFor: 'as yet unclassified' stamp: 'edc 3/9/2005 05:38'!
defaultGeneratorClass

	^ MacOSXGraphVizGenerator! !


Object subclass: #GraphVizGenerator
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GraphViz'!

!GraphVizGenerator methodsFor: 'generating' stamp: 'jrp 2/27/2005 21:58'!
runDotOn: graphviz outputFormat: aString

	self runOSProcess: #dot with: (self commandLineFor: graphviz outputFormat: aString)! !

!GraphVizGenerator methodsFor: 'generating' stamp: 'jrp 3/2/2005 23:30'!
runFdpOn: graphviz outputFormat: aString

	self runOSProcess: #fdp with: (self commandLineFor: graphviz outputFormat: aString)! !

!GraphVizGenerator methodsFor: 'generating' stamp: 'jrp 2/27/2005 21:57'!
runNeatoOn: graphviz outputFormat: aString

	self runOSProcess: #neato with: (self commandLineFor: graphviz outputFormat: aString)! !

!GraphVizGenerator methodsFor: 'generating' stamp: 'jrp 3/2/2005 23:30'!
runTwopiOn: graphviz outputFormat: aString

	self runOSProcess: #twopi with: (self commandLineFor: graphviz outputFormat: aString)! !


!GraphVizGenerator methodsFor: 'private' stamp: 'jrp 2/27/2005 21:53'!
runOSProcess: command with: arguments

	self subclassResponsibility! !


!GraphVizGenerator methodsFor: 'accessing' stamp: 'edc 3/9/2005 08:21'!
commandLineFor: graphviz outputFormat: outputFormat

	^ String streamContents:
		[:stream |
		stream
			
			nextPutAll: graphviz dotFileName;
			nextPutAll: ' -T';
			nextPutAll: outputFormat;
			nextPut: $ ;
			nextPutAll: ' -o ';
			nextPutAll: graphviz baseFileName;
			nextPut: $.;
			nextPutAll: outputFormat]! !


GraphVizGenerator subclass: #MacOSXGraphVizGenerator
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GraphViz'!

!MacOSXGraphVizGenerator methodsFor: 'accessing' stamp: 'edc 3/10/2005 10:49'!
commandLineFor: graphviz outputFormat: outputFormat

	^ String streamContents:
		[:stream |
		stream
			nextPutAll: self outputPath;
			nextPutAll: graphviz dotFileName;
			nextPutAll: ' -T';
			nextPutAll: outputFormat;
			nextPut: $ ;
			nextPutAll: ' -o';
			nextPutAll: self outputPath;
			nextPutAll: graphviz baseFileName;
			nextPut: $.;
			nextPutAll: outputFormat]! !

!MacOSXGraphVizGenerator methodsFor: 'accessing' stamp: 'edc 3/9/2005 07:53'!
outputFileFor: aExtension
 | s |
s _ self name ,aExtension! !

!MacOSXGraphVizGenerator methodsFor: 'accessing' stamp: 'edc 3/10/2005 17:13'!
outputPath 
	
	 
	
| aPath prefix |
aPath :=  FileDirectory default pathName,FileDirectory slash.
	prefix := self isCarbonVM ifTrue: [ '/Volumes/' ] ifFalse: [ '' ]. 
	^  prefix, (aPath copyReplaceAll: $: asString with: $/ asString)! !


!MacOSXGraphVizGenerator methodsFor: 'external calls' stamp: 'dd 3/8/2005 18:26'!
runWithLibC: string
	<cdecl: short 'system' (char*) module: '/usr/lib/libc.dylib'>
	^self externalCallFailed ! !

!MacOSXGraphVizGenerator methodsFor: 'external calls' stamp: 'dd 3/8/2005 18:25'!
runWithSystemFramework: string
	<apicall: short 'system' (char*) module: 'System.framework'>
	^self externalCallFailed ! !


!MacOSXGraphVizGenerator methodsFor: 'private' stamp: 'dd 3/8/2005 18:52'!
isCarbonVM
	^(SmalltalkImage current platformName = 'Mac OS' and: [ SmalltalkImage current osVersion asNumber >= 1000 ])! !

!MacOSXGraphVizGenerator methodsFor: 'private' stamp: 'edc 3/10/2005 17:08'!
posixPathFor
	"Answer a path which can be used with the system function from libc or System.framework."
	| prefix aPath |
	"Put /Volumes/ in front of the volume name. eg /Volumes/Macintosh HD"
	
	aPath _ self outputPath .
	prefix := self isCarbonVM ifTrue: [ '/Volumes/' ] ifFalse: [ '' ]. 
	^ $" asString, prefix, (aPath copyReplaceAll: $: asString with: $/ asString), $" asString.! !

!MacOSXGraphVizGenerator methodsFor: 'private' stamp: 'dd 3/8/2005 20:19'!
posixPathFor: aPath
	"Answer a path which can be used with the system function from libc or System.framework."
	| prefix |
	"Put /Volumes/ in front of the volume name. eg /Volumes/Macintosh HD"
	prefix _ self isCarbonVM ifTrue: [ '/Volumes/' ] ifFalse: [ '' ]. 
	^ $" asString, prefix, (aPath copyReplaceAll: $: asString with: $/ asString), $" asString.! !

!MacOSXGraphVizGenerator methodsFor: 'private' stamp: 'edc 3/10/2005 16:37'!
runOSProcess: command with: arguments 
| helperCommand |
	helperCommand := '/Applications/Graphviz.app/Contents/MacOS/', command.
	helperCommand := helperCommand, ' ', arguments.
Transcript show: helperCommand;cr.
	self isCarbonVM ifTrue: [ self runWithSystemFramework: helperCommand. ] ifFalse: [ self runWithLibC: helperCommand. ].! !

!MacOSXGraphVizGenerator methodsFor: 'private' stamp: 'edc 3/10/2005 16:36'!
runOSProcess: command with: arguments inDir: path
	| helperCommand |
	helperCommand := '/Applications/Graphviz.app/Contents/MacOS/', command.
	helperCommand := helperCommand, ' ', arguments.
Transcript show: helperCommand;cr.
	self isCarbonVM ifTrue: [ self runWithSystemFramework: helperCommand. ] ifFalse: [ self runWithLibC: helperCommand. ].! !


!MacOSXGraphVizGenerator methodsFor: 'generating' stamp: 'dd 3/8/2005 20:23'!
convertDotToXml: inputFile
	| xmlFile commandLine |
	xmlFile _ self outputFileFor: #xml.
	commandLine _ String streamContents:
		[:stream |
		stream
			nextPutAll: '-o';
			nextPutAll: (self posixPathFor: xmlFile);
			nextPut: $ ;
			nextPutAll: (self posixPathFor: inputFile)
		].

	self
		runOSProcess: #cvtgxl 
		with: commandLine
		inDir: self outputPath fullName.
		
	^ xmlFile! !


GraphVizGenerator subclass: #Win32GraphVizGenerator
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'GraphViz'!

!Win32GraphVizGenerator methodsFor: 'private' stamp: 'jrp 2/27/2005 21:53'!
runOSProcess: command with: arguments

	| shell |
	shell _ Win32Shell new.
	shell
		shellExecute: nil
		lpOperation: 'open'
		lpFile: command
		lpParameters: arguments
		lpDirectory: nil
		nShowCmd: 0! !


More information about the Squeak-dev mailing list