luquet-ruby-graphviz 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS +7 -0
- data/COPYING +340 -0
- data/ChangeLog +66 -0
- data/README.rdoc +69 -0
- data/bin/ruby2gv +194 -0
- data/examples/HTML-Labels.rb +20 -0
- data/examples/arrowhead.rb +97 -0
- data/examples/dot/cluster.dot +31 -0
- data/examples/dot/fsm.dot +20 -0
- data/examples/dot/genetic.dot +118 -0
- data/examples/dot/hello.dot +1 -0
- data/examples/dot/hello_test.rb +14 -0
- data/examples/dot/lion_share.dot +103 -0
- data/examples/dot/prof.dot +150 -0
- data/examples/dot/psg.dot +28 -0
- data/examples/dot/sdh.dot +284 -0
- data/examples/dot/siblings.dot +492 -0
- data/examples/dot/test.dot +17 -0
- data/examples/dot/unix.dot +104 -0
- data/examples/graphviz.org/TrafficLights.rb +62 -0
- data/examples/graphviz.org/cluster.rb +62 -0
- data/examples/graphviz.org/hello_world.rb +10 -0
- data/examples/graphviz.org/lion_share.rb +215 -0
- data/examples/graphviz.org/process.rb +37 -0
- data/examples/maketest.sh +85 -0
- data/examples/p2p.rb +35 -0
- data/examples/sample01.rb +32 -0
- data/examples/sample02.rb +42 -0
- data/examples/sample03.rb +31 -0
- data/examples/sample04.rb +22 -0
- data/examples/sample05.rb +32 -0
- data/examples/sample06.rb +46 -0
- data/examples/sample07.rb +23 -0
- data/examples/sample08.rb +34 -0
- data/examples/sample09.rb +50 -0
- data/examples/sample10.rb +50 -0
- data/examples/sample11.rb +42 -0
- data/examples/sample12.rb +55 -0
- data/examples/sample13.rb +48 -0
- data/examples/sample14.rb +44 -0
- data/examples/sample15.rb +23 -0
- data/examples/sample16.rb +8 -0
- data/examples/sample17.rb +92 -0
- data/examples/sample18.rb +24 -0
- data/examples/sample19.rb +59 -0
- data/examples/sample20.rb +47 -0
- data/examples/sample21.rb +12 -0
- data/examples/sample22.rb +10 -0
- data/examples/sample23.rb +11 -0
- data/examples/sample24.rb +11 -0
- data/examples/sample25.rb +11 -0
- data/examples/shapes.rb +24 -0
- data/examples/test.xml +26 -0
- data/examples/testorder.rb +43 -0
- data/examples/testxml.rb +7 -0
- data/lib/graphviz.rb +655 -0
- data/lib/graphviz/attrs.rb +51 -0
- data/lib/graphviz/constants.rb +246 -0
- data/lib/graphviz/dot.treetop +97 -0
- data/lib/graphviz/edge.rb +130 -0
- data/lib/graphviz/node.rb +129 -0
- data/lib/graphviz/parser.rb +249 -0
- data/lib/graphviz/xml.rb +131 -0
- data/setup.rb +1585 -0
- metadata +176 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$:.unshift( "../lib" );
|
4
|
+
require "graphviz"
|
5
|
+
|
6
|
+
GraphViz::options( :use => "dot" )
|
7
|
+
|
8
|
+
if ARGV[0]
|
9
|
+
GraphViz::options( :path => ARGV[0] )
|
10
|
+
end
|
11
|
+
|
12
|
+
GraphViz::new( "g", :rankdir => "LR", :type => "digraph" ) { |g|
|
13
|
+
g.node[:fontsize] = "16"
|
14
|
+
g.node[:shape] = "record"
|
15
|
+
|
16
|
+
g.node0( :label => "<f0> 0x10ba8| <f1>" )
|
17
|
+
g.node1( :label => "<f0> 0xf7fc4380| <f1> | <f2> |-1" )
|
18
|
+
g.node2( :label => "<f0> 0xf7fc44b8| | |2" )
|
19
|
+
g.node3( :label => "<f0> 3.43322790286038071e-06|44.79998779296875|0" )
|
20
|
+
g.node4( :label => "<f0> 0xf7fc4380| <f1> | <f2> |2" )
|
21
|
+
g.node5( :label => "<f0> (nil)| | |-1" )
|
22
|
+
g.node6( :label => "<f0> 0xf7fc4380| <f1> | <f2> |1" )
|
23
|
+
g.node7( :label => "<f0> 0xf7fc4380| <f1> | <f2> |2" )
|
24
|
+
g.node8( :label => "<f0> (nil)| | |-1" )
|
25
|
+
g.node9( :label => "<f0> (nil)| | |-1" )
|
26
|
+
g.node10( :label => "<f0> (nil)| <f1> | <f2> |-1" )
|
27
|
+
g.node11( :label => "<f0> (nil)| <f1> | <f2> |-1" )
|
28
|
+
g.node12( :label => "<f0> 0xf7fc43e0| | |1" )
|
29
|
+
|
30
|
+
g.add_edge( g.node0(:f0), g.node1(:f0) )
|
31
|
+
g.add_edge( g.node0(:f1), g.node2(:f0) )
|
32
|
+
g.add_edge( g.node1(:f0), g.node3(:f0) )
|
33
|
+
g.add_edge( g.node1(:f1), g.node4(:f0) )
|
34
|
+
g.add_edge( g.node1(:f2), g.node5(:f0) )
|
35
|
+
g.add_edge( g.node4(:f0), g.node3(:f0) )
|
36
|
+
g.add_edge( g.node4(:f1), g.node6(:f0) )
|
37
|
+
g.add_edge( g.node4(:f2), g.node10(:f0) )
|
38
|
+
g.add_edge( g.node6(:f0), g.node3(:f0) )
|
39
|
+
g.add_edge( g.node6(:f1), g.node7(:f0) )
|
40
|
+
g.add_edge( g.node6(:f2), g.node9(:f0) )
|
41
|
+
g.add_edge( g.node7(:f0), g.node3(:f0) )
|
42
|
+
g.add_edge( g.node7(:f1), g.node1(:f0) )
|
43
|
+
g.add_edge( g.node7(:f2), g.node8(:f0) )
|
44
|
+
g.add_edge( g.node10(:f1), g.node11(:f0) )
|
45
|
+
g.add_edge( g.node10(:f2), g.node12(:f0) )
|
46
|
+
g.add_edge( g.node11(:f2), g.node1(:f0) )
|
47
|
+
}.output( :png => "#{$0}.png" )
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$:.unshift( "../lib" );
|
4
|
+
require "graphviz"
|
5
|
+
|
6
|
+
{"png" => "#{$0}.png", "imap" => "#{$0}.html"}.each do |format, file|
|
7
|
+
GraphViz::new( "G" ) { |g|
|
8
|
+
g.command(:URL => "http://www.research.att.com/base.html")
|
9
|
+
g._output(:label => "output", :URL => "colors.html")
|
10
|
+
g.command << g._output
|
11
|
+
}.output( format => file )
|
12
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$:.unshift( "../lib" );
|
4
|
+
require "graphviz"
|
5
|
+
|
6
|
+
GraphViz::new( "mainmap" ) { |graph|
|
7
|
+
graph[:URL] = "http://www.research.att.com/base.html"
|
8
|
+
graph.command( :URL => "http://www.research.att.com/command.html" )
|
9
|
+
(graph.command << graph._output( :label => "output" ))[:URL] = "colors.html"
|
10
|
+
}.output( :canon => nil, :cmapx => "#{$0}.html", :png => "#{$0}.png" )
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$:.unshift( "../lib" );
|
4
|
+
require "graphviz"
|
5
|
+
|
6
|
+
r = GraphViz::new( "mainmap" ) { |graph|
|
7
|
+
graph[:URL] = "http://www.research.att.com/base.html"
|
8
|
+
graph.command( :URL => "http://www.research.att.com/command.html" )
|
9
|
+
(graph.command << graph._output( :label => "output" ))[:URL] = "colors.html"
|
10
|
+
}
|
11
|
+
puts r.output( :cmapx => String, :png => "#{$0}.png" )
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$:.unshift( "../lib" );
|
4
|
+
require "graphviz"
|
5
|
+
|
6
|
+
GraphViz::new( "G" ) { |g|
|
7
|
+
g.hello << g.world
|
8
|
+
g.bonjour( :label => '"Bonjour"' ) - g.monde( :label => "Le\nmonde")
|
9
|
+
g.hola > g.mundo
|
10
|
+
g.holla >> g.welt
|
11
|
+
}.output( :path => "/usr/local/bin", :png => "#{$0}.png" )
|
data/examples/shapes.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$:.unshift( "../lib" );
|
4
|
+
require "graphviz"
|
5
|
+
|
6
|
+
g = nil
|
7
|
+
if ARGV[0]
|
8
|
+
g = GraphViz::new( "G", "path" => ARGV[0] )
|
9
|
+
else
|
10
|
+
g = GraphViz::new( "G" )
|
11
|
+
end
|
12
|
+
|
13
|
+
g.node["shape"] = "ellipse"
|
14
|
+
|
15
|
+
[ "box", "polygon", "ellipse", "circle", "point",
|
16
|
+
"egg", "triangle", "plaintext", "diamond", "trapezium",
|
17
|
+
"parallelogram", "house", "pentagon", "hexagon", "septagon", "octagon", "doublecircle",
|
18
|
+
"doubleoctagon", "tripleoctagon", "invtriangle", "invtrapezium", "invhouse",
|
19
|
+
"Mdiamond", "Msquare", "Mcircle", "rect", "rectangle", "none", "note", "tab", "folder",
|
20
|
+
"box3d", "component" ].each { |s|
|
21
|
+
g.add_node( s, "shape" => s )
|
22
|
+
}
|
23
|
+
|
24
|
+
g.output( :png => "shapes.png")
|
data/examples/test.xml
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
<inventory title="OmniCorp Store #45x10^3">
|
2
|
+
<section name="health">
|
3
|
+
<item upc="123456789" stock="12">
|
4
|
+
<name>Invisibility Cream</name>
|
5
|
+
<price>14.50</price>
|
6
|
+
<description>Makes you invisible</description>
|
7
|
+
</item>
|
8
|
+
<item upc="445322344" stock="18">
|
9
|
+
<name>Levitation Salve</name>
|
10
|
+
<price>23.99</price>
|
11
|
+
<description>Levitate yourself for up to 3 hours per application</description>
|
12
|
+
</item>
|
13
|
+
</section>
|
14
|
+
<section name="food">
|
15
|
+
<item upc="485672034" stock="653">
|
16
|
+
<name>Blork and Freen Instameal</name>
|
17
|
+
<price>4.95</price>
|
18
|
+
<description>A tasty meal in a tablet; just add water</description>
|
19
|
+
</item>
|
20
|
+
<item upc="132957764" stock="44">
|
21
|
+
<name>Grob winglets</name>
|
22
|
+
<price>3.56</price>
|
23
|
+
<description>Tender winglets of Grob. Just add water</description>
|
24
|
+
</item>
|
25
|
+
</section>
|
26
|
+
</inventory>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$:.unshift( "../lib" );
|
4
|
+
require "graphviz"
|
5
|
+
|
6
|
+
g = nil
|
7
|
+
if ARGV[0]
|
8
|
+
g = GraphViz::new( "G", "path" => ARGV[0] )
|
9
|
+
else
|
10
|
+
g = GraphViz::new( "G" )
|
11
|
+
end
|
12
|
+
|
13
|
+
g.node["color"] = "black"
|
14
|
+
|
15
|
+
g.edge["color"] = "black"
|
16
|
+
g.edge["weight"] = "1"
|
17
|
+
g.edge["style"] = "filled"
|
18
|
+
g.edge["label"] = ""
|
19
|
+
|
20
|
+
g["size"] = "4,4"
|
21
|
+
|
22
|
+
g.node["shape"] = "box"
|
23
|
+
main = g.add_node( "main" )
|
24
|
+
g.node["shape"] = "ellipse"
|
25
|
+
parse = g.add_node( "parse" )
|
26
|
+
execute = g.add_node( "execute" )
|
27
|
+
init = g.add_node( "init" )
|
28
|
+
cleanup = g.add_node( "cleanup" )
|
29
|
+
make_string = g.add_node( "make_string", "label" => 'make a\nstring' )
|
30
|
+
printf = g.add_node( "printf" )
|
31
|
+
compare = g.add_node( "compare", "shape" => "box", "style" => "filled", "color" => ".7 .3 1.0" )
|
32
|
+
|
33
|
+
g.add_edge( main, parse, "weight" => "8" )
|
34
|
+
g.add_edge( parse, execute )
|
35
|
+
g.add_edge( main, init, "style" => "dotted" )
|
36
|
+
g.add_edge( main, cleanup )
|
37
|
+
g.add_edge( execute, make_string )
|
38
|
+
g.add_edge( execute, printf )
|
39
|
+
g.add_edge( init, make_string )
|
40
|
+
g.add_edge( main, printf, "color" => "red", "style" => "bold", "label" => "100 times" )
|
41
|
+
g.add_edge( execute, compare, "color" => "red" )
|
42
|
+
|
43
|
+
g.output( :png => "#{$0}.png" )
|
data/examples/testxml.rb
ADDED
data/lib/graphviz.rb
ADDED
@@ -0,0 +1,655 @@
|
|
1
|
+
# Copyright (C) 2003, 2004, 2005, 2006, 2007 Gregoire Lejeune <gregoire.lejeune@free.fr>
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation; either version 2 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
16
|
+
|
17
|
+
require 'tempfile'
|
18
|
+
#require 'mkmf'
|
19
|
+
|
20
|
+
require 'graphviz/node'
|
21
|
+
require 'graphviz/edge'
|
22
|
+
require 'graphviz/attrs'
|
23
|
+
require 'graphviz/constants'
|
24
|
+
require 'graphviz/parser'
|
25
|
+
|
26
|
+
class GraphViz
|
27
|
+
include Constants
|
28
|
+
|
29
|
+
public
|
30
|
+
|
31
|
+
## Var: Output format (dot, png, jpeg, ...)
|
32
|
+
@@format = nil #"canon"
|
33
|
+
@format
|
34
|
+
## Var: Output file name
|
35
|
+
@filename
|
36
|
+
## Var: Output format and file
|
37
|
+
@output
|
38
|
+
## Var: program to use (dot|twopi)
|
39
|
+
@@prog = "dot"
|
40
|
+
@prog
|
41
|
+
## Var: program path
|
42
|
+
@@path = nil
|
43
|
+
@path
|
44
|
+
## Var: Error level
|
45
|
+
@@errors = 1
|
46
|
+
@errors
|
47
|
+
|
48
|
+
## Var: Graph name
|
49
|
+
@name
|
50
|
+
|
51
|
+
## Var: defined attributs
|
52
|
+
@graph
|
53
|
+
@node
|
54
|
+
@edge
|
55
|
+
|
56
|
+
# This accessor allow you to set global nodes attributs
|
57
|
+
attr_accessor :node
|
58
|
+
|
59
|
+
# This accessor allow you to set global edges attributs
|
60
|
+
attr_accessor :edge
|
61
|
+
|
62
|
+
@elements_order
|
63
|
+
|
64
|
+
##
|
65
|
+
# Create a new node
|
66
|
+
#
|
67
|
+
# In:
|
68
|
+
# * xNodeName : Name of the new node
|
69
|
+
# * *hOpt : Node attributs
|
70
|
+
#
|
71
|
+
# Return the GraphViz::Node object created
|
72
|
+
#
|
73
|
+
def add_node( xNodeName, *hOpt )
|
74
|
+
@hoNodes[xNodeName] = GraphViz::Node::new( xNodeName, self )
|
75
|
+
|
76
|
+
if hOpt.nil? == false and hOpt[0].nil? == false
|
77
|
+
hOpt[0].each do |xKey, xValue|
|
78
|
+
@hoNodes[xNodeName][xKey.to_s] = xValue
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
@elements_order.push( {
|
83
|
+
"type" => "node",
|
84
|
+
"name" => xNodeName,
|
85
|
+
"value" => @hoNodes[xNodeName]
|
86
|
+
} )
|
87
|
+
|
88
|
+
return( @hoNodes[xNodeName] )
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# Return the node object for the given name (or nil)
|
93
|
+
#
|
94
|
+
def get_node( xNodeName, &block )
|
95
|
+
node = @hoNodes[xNodeName] || nil
|
96
|
+
|
97
|
+
yield( node ) if( block and node.nil? == false )
|
98
|
+
|
99
|
+
return node
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Create a new edge
|
104
|
+
#
|
105
|
+
# In:
|
106
|
+
# * oNodeOne : First node (or node list)
|
107
|
+
# * oNodeTwo : Second Node (or node list)
|
108
|
+
# * *hOpt : Edge attributs
|
109
|
+
#
|
110
|
+
def add_edge( oNodeOne, oNodeTwo, *hOpt )
|
111
|
+
|
112
|
+
if( oNodeOne.class == Array )
|
113
|
+
oNodeOne.each do |no|
|
114
|
+
add_edge( no, oNodeTwo, *hOpt )
|
115
|
+
end
|
116
|
+
else
|
117
|
+
if( oNodeTwo.class == Array )
|
118
|
+
oNodeTwo.each do |nt|
|
119
|
+
add_edge( oNodeOne, nt, *hOpt )
|
120
|
+
end
|
121
|
+
else
|
122
|
+
|
123
|
+
oEdge = GraphViz::Edge::new( oNodeOne, oNodeTwo, self )
|
124
|
+
|
125
|
+
if hOpt.nil? == false and hOpt[0].nil? == false
|
126
|
+
hOpt[0].each do |xKey, xValue|
|
127
|
+
oEdge[xKey.to_s] = xValue
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
@elements_order.push( {
|
132
|
+
"type" => "edge",
|
133
|
+
"value" => oEdge
|
134
|
+
} )
|
135
|
+
@loEdges.push( oEdge )
|
136
|
+
|
137
|
+
return( oEdge )
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Create a new graph
|
144
|
+
#
|
145
|
+
# In:
|
146
|
+
# * xGraphName : Graph name
|
147
|
+
# * *hOpt : Graph attributs
|
148
|
+
#
|
149
|
+
def add_graph( xGraphName, *hOpt )
|
150
|
+
@hoGraphs[xGraphName] = GraphViz::new( xGraphName, :parent => self, :type => @oGraphType )
|
151
|
+
|
152
|
+
if hOpt.nil? == false and hOpt[0].nil? == false
|
153
|
+
hOpt[0].each do |xKey, xValue|
|
154
|
+
@hoGraphs[xGraphName][xKey.to_s] = xValue
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
@elements_order.push( {
|
159
|
+
"type" => "graph",
|
160
|
+
"name" => xGraphName,
|
161
|
+
"value" => @hoGraphs[xGraphName]
|
162
|
+
} )
|
163
|
+
|
164
|
+
return( @hoGraphs[xGraphName] )
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# Return the graph object for the given name (or nil)
|
169
|
+
#
|
170
|
+
def get_graph( xGraphName, &block )
|
171
|
+
graph = @hoGraphs[xGraphName] || nil
|
172
|
+
|
173
|
+
yield( graph ) if( block and graph.nil? == false )
|
174
|
+
|
175
|
+
return graph
|
176
|
+
end
|
177
|
+
|
178
|
+
#
|
179
|
+
# Get the number of nodes
|
180
|
+
#
|
181
|
+
def node_count
|
182
|
+
@hoNodes.size
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# Get the number of edges
|
187
|
+
#
|
188
|
+
def edge_count
|
189
|
+
@loEdges.size
|
190
|
+
end
|
191
|
+
|
192
|
+
def method_missing( idName, *args, &block ) #:nodoc:
|
193
|
+
xName = idName.id2name
|
194
|
+
|
195
|
+
rCod = nil
|
196
|
+
|
197
|
+
if block
|
198
|
+
# Creating a cluster named '#{xName}'
|
199
|
+
rCod = add_graph( xName, args[0] )
|
200
|
+
yield( rCod )
|
201
|
+
else
|
202
|
+
# Create a node named '#{xName}' or search for a node, edge or cluster
|
203
|
+
if @hoNodes.keys.include?( xName )
|
204
|
+
if( args[0] )
|
205
|
+
return "#{xName}:#{args[0].to_s}"
|
206
|
+
else
|
207
|
+
return( @hoNodes[xName] )
|
208
|
+
end
|
209
|
+
end
|
210
|
+
return( @hoGraphs[xName] ) if @hoGraphs.keys.include?( xName )
|
211
|
+
|
212
|
+
rCod = add_node( xName, args[0] )
|
213
|
+
end
|
214
|
+
|
215
|
+
return rCod
|
216
|
+
end
|
217
|
+
|
218
|
+
#
|
219
|
+
# Set value +xValue+ to the graph attribut +xAttrName+
|
220
|
+
#
|
221
|
+
def []=( xAttrName, xValue )
|
222
|
+
xValue = xValue.to_s if xValue.class == Symbol
|
223
|
+
@graph[xAttrName] = xValue
|
224
|
+
end
|
225
|
+
|
226
|
+
#
|
227
|
+
# Get the value of the graph attribut +xAttrName+
|
228
|
+
#
|
229
|
+
def []( xAttrName )
|
230
|
+
return( @graph[xAttrName].clone )
|
231
|
+
end
|
232
|
+
|
233
|
+
#
|
234
|
+
# Generate the graph
|
235
|
+
#
|
236
|
+
# Options :
|
237
|
+
# * :output : Output format (Constants::FORMATS)
|
238
|
+
# * :file : Output file name
|
239
|
+
# * :use : Program to use (Constants::PROGRAMS)
|
240
|
+
# * :path : Program PATH
|
241
|
+
# * :<format> => <file> : <file> can be
|
242
|
+
# * a file name
|
243
|
+
# * nil, then the output will be printed to STDOUT
|
244
|
+
# * String, then the output will be returned as a String
|
245
|
+
# * :errors : DOT error level (default 1)
|
246
|
+
# * 0 = Error + Warning
|
247
|
+
# * 1 = Error
|
248
|
+
# * 2 = none
|
249
|
+
#
|
250
|
+
def output( *hOpt )
|
251
|
+
xDOTScript = ""
|
252
|
+
xLastType = nil
|
253
|
+
xSeparator = ""
|
254
|
+
xData = ""
|
255
|
+
|
256
|
+
@elements_order.each { |kElement|
|
257
|
+
if xLastType.nil? == true or xLastType != kElement["type"]
|
258
|
+
|
259
|
+
if xData.length > 0
|
260
|
+
case xLastType
|
261
|
+
when "graph_attr"
|
262
|
+
xDOTScript << " " + xData + ";\n"
|
263
|
+
|
264
|
+
when "node_attr"
|
265
|
+
xDOTScript << " node [" + xData + "];\n"
|
266
|
+
|
267
|
+
when "edge_attr"
|
268
|
+
xDOTScript << " edge [" + xData + "];\n"
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
xSeparator = ""
|
273
|
+
xData = ""
|
274
|
+
end
|
275
|
+
|
276
|
+
xLastType = kElement["type"]
|
277
|
+
|
278
|
+
#Modified by
|
279
|
+
#Brandon Coleman
|
280
|
+
#verify value is NOT NULL
|
281
|
+
if kElement["value"] == nil then
|
282
|
+
raise ArgumentError, "#{kElement["name"]} has a nil value!"
|
283
|
+
end
|
284
|
+
|
285
|
+
case kElement["type"]
|
286
|
+
when "graph_attr"
|
287
|
+
xData << xSeparator + kElement["name"] + " = \"" + kElement["value"] + "\""
|
288
|
+
xSeparator = "; "
|
289
|
+
|
290
|
+
when "node_attr"
|
291
|
+
xData << xSeparator + kElement["name"] + " = \"" + kElement["value"] + "\""
|
292
|
+
xSeparator = ", "
|
293
|
+
|
294
|
+
when "edge_attr"
|
295
|
+
xData << xSeparator + kElement["name"] + " = \"" + kElement["value"] + "\""
|
296
|
+
xSeparator = ", "
|
297
|
+
|
298
|
+
when "node"
|
299
|
+
xDOTScript << " " + kElement["value"].output() + "\n"
|
300
|
+
|
301
|
+
when "edge"
|
302
|
+
xDOTScript << " " + kElement["value"].output( @oGraphType ) + "\n"
|
303
|
+
|
304
|
+
when "graph"
|
305
|
+
xDOTScript << kElement["value"].output() + "\n"
|
306
|
+
|
307
|
+
else
|
308
|
+
raise ArgumentError, "Don't know what to do with element type '#{kElement['type']}'"
|
309
|
+
end
|
310
|
+
}
|
311
|
+
|
312
|
+
if xData.length > 0
|
313
|
+
case xLastType
|
314
|
+
when "graph_attr"
|
315
|
+
xDOTScript << " " + xData + ";\n"
|
316
|
+
|
317
|
+
when "node_attr"
|
318
|
+
xDOTScript << " node [" + xData + "];\n"
|
319
|
+
|
320
|
+
when "edge_attr"
|
321
|
+
xDOTScript << " edge [" + xData + "];\n"
|
322
|
+
end
|
323
|
+
end
|
324
|
+
xDOTScript << "}"
|
325
|
+
|
326
|
+
if @oParentGraph.nil? == false
|
327
|
+
xDOTScript = "subgraph #{@name} {\n" << xDOTScript
|
328
|
+
|
329
|
+
return( xDOTScript )
|
330
|
+
else
|
331
|
+
if hOpt.nil? == false and hOpt[0].nil? == false
|
332
|
+
hOpt[0].each do |xKey, xValue|
|
333
|
+
xValue = xValue.to_s unless xValue.nil? or [Class, TrueClass, FalseClass].include?(xValue.class)
|
334
|
+
case xKey.to_s
|
335
|
+
when "output"
|
336
|
+
warn ":output option is deprecated, please use :<format> => :<file>"
|
337
|
+
if FORMATS.index( xValue ).nil? == true
|
338
|
+
raise ArgumentError, "output format '#{xValue}' invalid"
|
339
|
+
end
|
340
|
+
@format = xValue
|
341
|
+
when "file"
|
342
|
+
warn ":file option is deprecated, please use :<format> => :<file>"
|
343
|
+
@filename = xValue
|
344
|
+
when "use"
|
345
|
+
if PROGRAMS.index( xValue ).nil? == true
|
346
|
+
raise ArgumentError, "can't use '#{xValue}'"
|
347
|
+
end
|
348
|
+
@prog = xValue
|
349
|
+
when "path"
|
350
|
+
@path = xValue
|
351
|
+
when "errors"
|
352
|
+
@errors = xValue
|
353
|
+
else
|
354
|
+
if FORMATS.index( xKey.to_s ).nil? == true
|
355
|
+
raise ArgumentError, "output format '#{xValue}' invalid"
|
356
|
+
end
|
357
|
+
@output[xKey.to_s] = xValue
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
xDOTScript = "#{@oGraphType} #{@name} {\n" << xDOTScript
|
363
|
+
|
364
|
+
xOutputString = false
|
365
|
+
xOutput = if @format != "none"
|
366
|
+
## Act: Save script and send it to dot
|
367
|
+
t = if /Windows/.match( ENV['OS'] )
|
368
|
+
Tempfile::open( File.basename($0), "." )
|
369
|
+
else
|
370
|
+
Tempfile::open( File.basename($0) )
|
371
|
+
end
|
372
|
+
t.print( xDOTScript )
|
373
|
+
t.close
|
374
|
+
|
375
|
+
cmd = find_executable( )
|
376
|
+
if cmd == nil
|
377
|
+
raise StandardError, "GraphViz not installed or #{@prog} not in PATH. Install GraphViz or use the 'path' option"
|
378
|
+
end
|
379
|
+
|
380
|
+
xOutputWithFile = ""
|
381
|
+
xOutputWithoutFile = ""
|
382
|
+
unless @format.nil?
|
383
|
+
if @filename.nil?
|
384
|
+
xOutputWithoutFile = "-T#{@format} "
|
385
|
+
elsif @filename == String
|
386
|
+
xOutputWithoutFile = "-T#{@format} "
|
387
|
+
xOutputString = true
|
388
|
+
else
|
389
|
+
xOutputWithFile = "-T#{@format} -o#{@filename} "
|
390
|
+
end
|
391
|
+
end
|
392
|
+
@output.each do |format, file|
|
393
|
+
if file.nil?
|
394
|
+
xOutputWithoutFile << "-T#{format} "
|
395
|
+
elsif file == String
|
396
|
+
xOutputWithoutFile << "-T#{format} "
|
397
|
+
xOutputString = true
|
398
|
+
else
|
399
|
+
xOutputWithFile << "-T#{format} -o#{file} "
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
#xCmd = "#{cmd} #{xOutputWithFile} #{xOutputWithoutFile} #{t.path}"
|
404
|
+
#if /Windows/.match( ENV['OS'] )
|
405
|
+
xCmd = "\"#{cmd}\" -q#{@errors} #{xOutputWithFile} #{xOutputWithoutFile} #{t.path}"
|
406
|
+
#end
|
407
|
+
|
408
|
+
output_from_command( xCmd )
|
409
|
+
else
|
410
|
+
xDOTScript
|
411
|
+
end
|
412
|
+
|
413
|
+
if xOutputString
|
414
|
+
xOutput
|
415
|
+
else
|
416
|
+
print xOutput
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
alias :save :output
|
422
|
+
|
423
|
+
def output_and_errors_from_command(cmd) #:nodoc:
|
424
|
+
unless defined? Open3
|
425
|
+
begin
|
426
|
+
require 'open3'
|
427
|
+
require 'win32/open3'
|
428
|
+
rescue LoadError
|
429
|
+
end
|
430
|
+
end
|
431
|
+
begin
|
432
|
+
Open3.popen3( cmd ) do |stdin, stdout, stderr|
|
433
|
+
stdin.close
|
434
|
+
[stdout.read, stderr.read]
|
435
|
+
end
|
436
|
+
rescue NotImplementedError, NoMethodError
|
437
|
+
IO.popen( cmd ) do |stdout|
|
438
|
+
[stdout.read, nil]
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
def output_from_command(cmd) #:nodoc:
|
444
|
+
output, errors = output_and_errors_from_command(cmd)
|
445
|
+
if errors.nil? || errors.strip.empty?
|
446
|
+
output
|
447
|
+
else
|
448
|
+
raise "Error from #{cmd}:\n#{errors}"
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
#
|
453
|
+
# Get the graph name
|
454
|
+
#
|
455
|
+
def name
|
456
|
+
@name.clone
|
457
|
+
end
|
458
|
+
|
459
|
+
#
|
460
|
+
# Create an edge between the current cluster and the node or cluster +oNode+
|
461
|
+
#
|
462
|
+
def <<( oNode )
|
463
|
+
raise( ArgumentError, "Edge between root graph and node or cluster not allowed!" ) if self.pg.nil?
|
464
|
+
|
465
|
+
if( oNode.class == Array )
|
466
|
+
oNode.each do |no|
|
467
|
+
self << no
|
468
|
+
end
|
469
|
+
else
|
470
|
+
return GraphViz::commonGraph( oNode, self ).add_edge( self, oNode )
|
471
|
+
end
|
472
|
+
end
|
473
|
+
alias :> :<<
|
474
|
+
alias :- :<<
|
475
|
+
alias :>> :<<
|
476
|
+
|
477
|
+
def pg #:nodoc:
|
478
|
+
@oParentGraph
|
479
|
+
end
|
480
|
+
|
481
|
+
def self.commonGraph( o1, o2 ) #:nodoc:
|
482
|
+
g1 = o1.pg
|
483
|
+
g2 = o2.pg
|
484
|
+
|
485
|
+
return o1 if g1.nil?
|
486
|
+
return o2 if g2.nil?
|
487
|
+
|
488
|
+
return g1 if g1.object_id == g2.object_id
|
489
|
+
|
490
|
+
return GraphViz::commonGraph( g1, g2 )
|
491
|
+
end
|
492
|
+
|
493
|
+
def set_position( xType, xKey, xValue ) #:nodoc:
|
494
|
+
@elements_order.push( {
|
495
|
+
"type" => "#{xType}_attr",
|
496
|
+
"name" => xKey,
|
497
|
+
"value" => xValue
|
498
|
+
} )
|
499
|
+
end
|
500
|
+
|
501
|
+
## ----------------------------------------------------------------------------
|
502
|
+
|
503
|
+
#
|
504
|
+
# Change default options (:use, :path, :errors and :output)
|
505
|
+
#
|
506
|
+
def self.default( hOpts )
|
507
|
+
hOpts.each do |k, v|
|
508
|
+
case k.to_s
|
509
|
+
when "use"
|
510
|
+
@@prog = v
|
511
|
+
when "path"
|
512
|
+
@@path = v
|
513
|
+
when "errors"
|
514
|
+
@@errors = v
|
515
|
+
when "output"
|
516
|
+
warn ":output option is deprecated!"
|
517
|
+
@@format = v
|
518
|
+
else
|
519
|
+
warn "Invalide option #{k}!"
|
520
|
+
end
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
def self.options( hOpts )
|
525
|
+
GraphViz::default( hOpts )
|
526
|
+
end
|
527
|
+
|
528
|
+
## ----------------------------------------------------------------------------
|
529
|
+
|
530
|
+
#
|
531
|
+
# Create a new graph from a GraphViz File
|
532
|
+
#
|
533
|
+
# Options :
|
534
|
+
# * :output : Output format (Constants::FORMATS) (default : dot)
|
535
|
+
# * :file : Output file name (default : none)
|
536
|
+
# * :use : Program to use (Constants::PROGRAMS) (default : dot)
|
537
|
+
# * :path : Program PATH
|
538
|
+
# * :parent : Parent graph (default : none)
|
539
|
+
# * :type : Graph type (Constants::GRAPHTYPE) (default : digraph)
|
540
|
+
#
|
541
|
+
def self.parse( xFile, *hOpts, &block )
|
542
|
+
g = GraphViz::Parser.parse( xFile, hOpts[0], &block )
|
543
|
+
return g
|
544
|
+
end
|
545
|
+
|
546
|
+
## ----------------------------------------------------------------------------
|
547
|
+
|
548
|
+
private
|
549
|
+
|
550
|
+
## Var: Nodes, Edges and Graphs tables
|
551
|
+
@hoNodes
|
552
|
+
@loEdges
|
553
|
+
@hoGraphs
|
554
|
+
|
555
|
+
## Var: Parent graph
|
556
|
+
@oParentGraph
|
557
|
+
|
558
|
+
## Var: Type de graphe (orienté ou non)
|
559
|
+
@oGraphType
|
560
|
+
|
561
|
+
#
|
562
|
+
# Create a new graph object
|
563
|
+
#
|
564
|
+
# Options :
|
565
|
+
# * :output : Output format (Constants::FORMATS) (default : dot)
|
566
|
+
# * :file : Output file name (default : none)
|
567
|
+
# * :use : Program to use (Constants::PROGRAMS) (default : dot)
|
568
|
+
# * :path : Program PATH
|
569
|
+
# * :parent : Parent graph (default : none)
|
570
|
+
# * :type : Graph type (Constants::GRAPHTYPE) (default : digraph)
|
571
|
+
# * :errors : DOT error level (default 1)
|
572
|
+
# * 0 = Error + Warning
|
573
|
+
# * 1 = Error
|
574
|
+
# * 2 = none
|
575
|
+
#
|
576
|
+
def initialize( xGraphName, *hOpt, &block )
|
577
|
+
@filename = nil
|
578
|
+
@name = xGraphName.to_s
|
579
|
+
@format = @@format
|
580
|
+
@prog = @@prog
|
581
|
+
@path = @@path
|
582
|
+
@errors = @@errors
|
583
|
+
@output = {}
|
584
|
+
|
585
|
+
@elements_order = Array::new()
|
586
|
+
|
587
|
+
@oParentGraph = nil
|
588
|
+
@oGraphType = "digraph"
|
589
|
+
|
590
|
+
@hoNodes = Hash::new()
|
591
|
+
@loEdges = Array::new()
|
592
|
+
@hoGraphs = Hash::new()
|
593
|
+
|
594
|
+
@node = GraphViz::Attrs::new( self, "node", NODESATTRS )
|
595
|
+
@edge = GraphViz::Attrs::new( self, "edge", EDGESATTRS )
|
596
|
+
@graph = GraphViz::Attrs::new( self, "graph", GRAPHSATTRS )
|
597
|
+
|
598
|
+
if hOpt.nil? == false and hOpt[0].nil? == false
|
599
|
+
hOpt[0].each do |xKey, xValue|
|
600
|
+
case xKey.to_s
|
601
|
+
when "output"
|
602
|
+
warn ":output option is deprecated, please use :<format> => :<file>"
|
603
|
+
if FORMATS.index( xValue.to_s ).nil? == true
|
604
|
+
raise ArgumentError, "output format '#{xValue}' invalid"
|
605
|
+
end
|
606
|
+
@format = xValue.to_s
|
607
|
+
when "use"
|
608
|
+
if PROGRAMS.index( xValue.to_s ).nil? == true
|
609
|
+
raise ArgumentError, "can't use '#{xValue}'"
|
610
|
+
end
|
611
|
+
@prog = xValue.to_s
|
612
|
+
when "file"
|
613
|
+
warn ":file option is deprecated, please use :<format> => :<file>"
|
614
|
+
@filename = xValue.to_s
|
615
|
+
when "parent"
|
616
|
+
@oParentGraph = xValue
|
617
|
+
when "type"
|
618
|
+
if GRAPHTYPE.index( xValue.to_s ).nil? == true
|
619
|
+
raise ArgumentError, "graph type '#{xValue}' unknow"
|
620
|
+
end
|
621
|
+
@oGraphType = xValue.to_s
|
622
|
+
when "path"
|
623
|
+
@path = xValue.to_s
|
624
|
+
when "errors"
|
625
|
+
@errors = xValue
|
626
|
+
else
|
627
|
+
self[xKey.to_s] = xValue.to_s
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
yield( self ) if( block )
|
633
|
+
end
|
634
|
+
|
635
|
+
#
|
636
|
+
# Escape a string to be acceptable as a node name in a graphviz input file
|
637
|
+
#
|
638
|
+
def self.escape(str, force = false) #:nodoc:
|
639
|
+
if force or str.match( /\A[a-zA-Z_]+[a-zA-Z0-9_:\.]*\Z/ ).nil?
|
640
|
+
'"' + str.gsub('"', '\\"').gsub("\n", '\\\\n') + '"'
|
641
|
+
else
|
642
|
+
str
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
def find_executable( ) #:nodoc:
|
647
|
+
cmd = find_executable0( @prog )
|
648
|
+
if cmd == nil and @path != nil
|
649
|
+
__cmd = File.join( @path, @prog )
|
650
|
+
cmd = __cmd if File.executable?( __cmd )
|
651
|
+
end
|
652
|
+
return cmd
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|