ruby-graphviz 0.9.16 → 0.9.17
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 +1 -0
- data/README.rdoc +64 -0
- data/examples/graphml/attributes.ext.graphml +12 -0
- data/examples/graphml/attributes.graphml +40 -0
- data/examples/graphml/cluster.graphml +75 -0
- data/examples/graphml/hyper.graphml +29 -0
- data/examples/graphml/nested.graphml +54 -0
- data/examples/graphml/port.graphml +32 -0
- data/examples/graphml/simple.graphml +30 -0
- data/examples/sample57.rb +6 -0
- data/examples/theory/PERT.png +0 -0
- data/examples/theory/matrix.png +0 -0
- data/examples/theory/pert.rb +47 -0
- data/examples/theory/tests.rb +81 -0
- data/lib/graphviz.rb +45 -8
- data/lib/graphviz/constants.rb +43 -40
- data/lib/graphviz/edge.rb +22 -5
- data/lib/graphviz/elements.rb +39 -0
- data/lib/graphviz/graphml.rb +218 -0
- data/lib/graphviz/math/matrix.rb +221 -0
- data/lib/graphviz/node.rb +11 -0
- data/lib/graphviz/theory.rb +252 -0
- data/lib/graphviz/types.rb +4 -0
- data/lib/graphviz/types/gv_double.rb +20 -0
- metadata +24 -4
data/lib/graphviz.rb
CHANGED
@@ -25,6 +25,7 @@ require 'graphviz/node'
|
|
25
25
|
require 'graphviz/edge'
|
26
26
|
require 'graphviz/attrs'
|
27
27
|
require 'graphviz/constants'
|
28
|
+
require 'graphviz/elements'
|
28
29
|
|
29
30
|
require 'graphviz/dot2ruby'
|
30
31
|
require 'graphviz/types'
|
@@ -93,7 +94,8 @@ class GraphViz
|
|
93
94
|
#
|
94
95
|
def add_node( xNodeName, hOpts = {} )
|
95
96
|
@hoNodes[xNodeName] = GraphViz::Node::new( xNodeName, self )
|
96
|
-
|
97
|
+
@hoNodes[xNodeName].index = @elements_order.size_of( "node" )
|
98
|
+
|
97
99
|
unless hOpts.keys.include?(:label) or hOpts.keys.include?("label")
|
98
100
|
hOpts[:label] = xNodeName
|
99
101
|
end
|
@@ -122,12 +124,24 @@ class GraphViz
|
|
122
124
|
return node
|
123
125
|
end
|
124
126
|
|
127
|
+
#
|
128
|
+
# Return the node object for the given index
|
129
|
+
#
|
130
|
+
def get_node_at_index( index )
|
131
|
+
element = @elements_order[index, "node"]
|
132
|
+
(element.nil?) ? nil : element["value"]
|
133
|
+
end
|
134
|
+
|
125
135
|
#
|
126
136
|
# Allow you to traverse nodes
|
127
137
|
#
|
128
138
|
def each_node( &block )
|
129
|
-
|
130
|
-
|
139
|
+
if block_given?
|
140
|
+
@hoNodes.each do |name, node|
|
141
|
+
yield( name, node )
|
142
|
+
end
|
143
|
+
else
|
144
|
+
return( @hoNodes )
|
131
145
|
end
|
132
146
|
end
|
133
147
|
|
@@ -159,6 +173,7 @@ class GraphViz
|
|
159
173
|
end
|
160
174
|
else
|
161
175
|
oEdge = GraphViz::Edge::new( oNodeOne, oNodeTwo, self )
|
176
|
+
oEdge.index = @elements_order.size_of( "edge" )
|
162
177
|
|
163
178
|
hOpts.each do |xKey, xValue|
|
164
179
|
oEdge[xKey.to_s] = xValue
|
@@ -179,8 +194,12 @@ class GraphViz
|
|
179
194
|
# Allow you to traverse edges
|
180
195
|
#
|
181
196
|
def each_edge( &block )
|
182
|
-
|
183
|
-
|
197
|
+
if block_given?
|
198
|
+
@loEdges.each do |edge|
|
199
|
+
yield(edge)
|
200
|
+
end
|
201
|
+
else
|
202
|
+
return @loEdges
|
184
203
|
end
|
185
204
|
end
|
186
205
|
|
@@ -191,6 +210,14 @@ class GraphViz
|
|
191
210
|
@loEdges.size
|
192
211
|
end
|
193
212
|
|
213
|
+
#
|
214
|
+
# Return the edge object for the given index
|
215
|
+
#
|
216
|
+
def get_edge_at_index( index )
|
217
|
+
element = @elements_order[index, "edge"]
|
218
|
+
(element.nil?) ? nil : element["value"]
|
219
|
+
end
|
220
|
+
|
194
221
|
#
|
195
222
|
# Create a new graph
|
196
223
|
#
|
@@ -241,11 +268,21 @@ class GraphViz
|
|
241
268
|
# Allow you to traverse graphs
|
242
269
|
#
|
243
270
|
def each_graph( &block )
|
244
|
-
|
245
|
-
|
271
|
+
if block_given?
|
272
|
+
@hoGraphs.each do |name, graph|
|
273
|
+
yield( name, graph )
|
274
|
+
end
|
275
|
+
else
|
276
|
+
return @hoGraphs
|
246
277
|
end
|
247
278
|
end
|
248
279
|
|
280
|
+
#
|
281
|
+
# Return the graph type (graph digraph)
|
282
|
+
def type
|
283
|
+
@oGraphType
|
284
|
+
end
|
285
|
+
|
249
286
|
#
|
250
287
|
# Get the number of graphs
|
251
288
|
#
|
@@ -669,7 +706,7 @@ class GraphViz
|
|
669
706
|
@output = {}
|
670
707
|
@nothugly = false
|
671
708
|
|
672
|
-
@elements_order =
|
709
|
+
@elements_order = GraphViz::Elements::new()
|
673
710
|
|
674
711
|
@oParentGraph = nil
|
675
712
|
@oGraphType = "digraph"
|
data/lib/graphviz/constants.rb
CHANGED
@@ -40,7 +40,7 @@
|
|
40
40
|
# C => cluster
|
41
41
|
#
|
42
42
|
module Constants
|
43
|
-
RGV_VERSION = "0.9.
|
43
|
+
RGV_VERSION = "0.9.17"
|
44
44
|
|
45
45
|
## Const: Output formats
|
46
46
|
FORMATS = [
|
@@ -96,7 +96,8 @@ module Constants
|
|
96
96
|
"neato",
|
97
97
|
"twopi",
|
98
98
|
"fdp",
|
99
|
-
"circo"
|
99
|
+
"circo",
|
100
|
+
"sfdp"
|
100
101
|
]
|
101
102
|
|
102
103
|
## Const: graphs type
|
@@ -115,11 +116,11 @@ module Constants
|
|
115
116
|
|
116
117
|
# E, N, G, S and C represent edges, nodes, the root graph, subgraphs and cluster subgraphs, respectively
|
117
118
|
GENCS_ATTRS = {
|
118
|
-
"Damping" => { :usedBy => "G", :type => :
|
119
|
-
"K" => { :usedBy => "GC", :type => :
|
119
|
+
"Damping" => { :usedBy => "G", :type => :GvDouble },
|
120
|
+
"K" => { :usedBy => "GC", :type => :GvDouble },
|
120
121
|
"URL" => { :usedBy => "ENGC", :type => :EscString },
|
121
122
|
"arrowhead" => { :usedBy => "E", :type => :EscString },
|
122
|
-
"arrowsize" => { :usedBy => "E", :type => :
|
123
|
+
"arrowsize" => { :usedBy => "E", :type => :GvDouble },
|
123
124
|
"arrowtail" => { :usedBy => "E", :type => :EscString },
|
124
125
|
"aspect" => { :usedBy => "G", :type => :EscString },
|
125
126
|
"bb" => { :usedBy => "G", :type => :EscString },
|
@@ -134,26 +135,26 @@ module Constants
|
|
134
135
|
"concentrate" => { :usedBy => "G", :type => :EscString },
|
135
136
|
"constraint" => { :usedBy => "E", :type => :EscString },
|
136
137
|
"decorate" => { :usedBy => "E", :type => :EscString },
|
137
|
-
"defaultdist" => { :usedBy => "G", :type => :
|
138
|
+
"defaultdist" => { :usedBy => "G", :type => :GvDouble },
|
138
139
|
"dim" => { :usedBy => "G", :type => :EscString },
|
139
140
|
"dimen" => { :usedBy => "G", :type => :EscString },
|
140
141
|
"dir" => { :usedBy => "E", :type => :EscString },
|
141
142
|
"diredgeconstraints" => { :usedBy => "G", :type => :EscString },
|
142
|
-
"distortion" => { :usedBy => "N", :type => :
|
143
|
-
"dpi" => { :usedBy => "G", :type => :
|
143
|
+
"distortion" => { :usedBy => "N", :type => :GvDouble },
|
144
|
+
"dpi" => { :usedBy => "G", :type => :GvDouble },
|
144
145
|
"edgeURL" => { :usedBy => "E", :type => :EscString },
|
145
146
|
"edgehref" => { :usedBy => "E", :type => :EscString },
|
146
147
|
"edgetarget" => { :usedBy => "E", :type => :EscString },
|
147
148
|
"edgetooltip" => { :usedBy => "E", :type => :EscString },
|
148
|
-
"epsilon" => { :usedBy => "G", :type => :
|
149
|
-
"esep" => { :usedBy => "G", :type => :EscString },
|
149
|
+
"epsilon" => { :usedBy => "G", :type => :GvDouble },
|
150
|
+
"esep" => { :usedBy => "G", :type => :EscString }, # GvDouble , pointf
|
150
151
|
"fillcolor" => { :usedBy => "NC", :type => :EscString },
|
151
152
|
"fixedsize" => { :usedBy => "N", :type => :EscString },
|
152
153
|
"fontcolor" => { :usedBy => "ENGC", :type => :EscString },
|
153
154
|
"fontname" => { :usedBy => "ENGC", :type => :EscString },
|
154
155
|
"fontnames" => { :usedBy => "G", :type => :EscString },
|
155
156
|
"fontpath" => { :usedBy => "G", :type => :EscString },
|
156
|
-
"fontsize" => { :usedBy => "ENGC", :type => :
|
157
|
+
"fontsize" => { :usedBy => "ENGC", :type => :GvDouble },
|
157
158
|
"group" => { :usedBy => "N", :type => :EscString },
|
158
159
|
"headURL" => { :usedBy => "E", :type => :EscString },
|
159
160
|
"headclip" => { :usedBy => "E", :type => :EscString },
|
@@ -162,7 +163,7 @@ module Constants
|
|
162
163
|
"headport" => { :usedBy => "E", :type => :EscString },
|
163
164
|
"headtarget" => { :usedBy => "E", :type => :EscString },
|
164
165
|
"headtooltip" => { :usedBy => "E", :type => :EscString },
|
165
|
-
"height" => { :usedBy => "N", :type => :
|
166
|
+
"height" => { :usedBy => "N", :type => :GvDouble },
|
166
167
|
"href" => { :usedBy => "NE", :type => :EscString },
|
167
168
|
"html" => { :usedBy => "N", :type => :HtmlString }, # API extension -- Deprecated
|
168
169
|
"id" => { :usedBy => "ENG", :type => :EscString },
|
@@ -170,12 +171,12 @@ module Constants
|
|
170
171
|
"imagescale" => { :usedBy => "N", :type => :EscString },
|
171
172
|
"label" => { :usedBy => "ENGC", :type => :LblString },
|
172
173
|
"labelURL" => { :usedBy => "E", :type => :EscString },
|
173
|
-
"labelangle" => { :usedBy => "E", :type => :
|
174
|
-
"labeldistance" => { :usedBy => "E", :type => :
|
174
|
+
"labelangle" => { :usedBy => "E", :type => :GvDouble },
|
175
|
+
"labeldistance" => { :usedBy => "E", :type => :GvDouble },
|
175
176
|
"labelfloat" => { :usedBy => "E", :type => :EscString },
|
176
177
|
"labelfontcolor" => { :usedBy => "E", :type => :EscString },
|
177
178
|
"labelfontname" => { :usedBy => "E", :type => :EscString },
|
178
|
-
"labelfontsize" => { :usedBy => "E", :type => :
|
179
|
+
"labelfontsize" => { :usedBy => "E", :type => :GvDouble },
|
179
180
|
"labelhref" => { :usedBy => "E", :type => :EscString },
|
180
181
|
"labeljust" => { :usedBy => "GC", :type => :EscString },
|
181
182
|
"labelloc" => { :usedBy => "GCN", :type => :EscString },
|
@@ -186,64 +187,66 @@ module Constants
|
|
186
187
|
"layers" => { :usedBy => "G", :type => :EscString },
|
187
188
|
"layersep" => { :usedBy => "G", :type => :EscString },
|
188
189
|
"layout" => { :usedBy => "G", :type => :EscString },
|
189
|
-
"len" => { :usedBy => "E", :type => :
|
190
|
+
"len" => { :usedBy => "E", :type => :GvDouble },
|
190
191
|
"levels" => { :usedBy => "G", :type => :EscString },
|
191
|
-
"levelsgap" => { :usedBy => "G", :type => :
|
192
|
+
"levelsgap" => { :usedBy => "G", :type => :GvDouble },
|
192
193
|
"lhead" => { :usedBy => "E", :type => :EscString },
|
194
|
+
"lheight" => { :usedBy => "GC", :type => :GvDouble },
|
193
195
|
"lp" => { :usedBy => "EGC", :type => :EscString },
|
194
196
|
"ltail" => { :usedBy => "E", :type => :EscString },
|
195
|
-
"
|
197
|
+
"lweight" => { :usedBy => "GC", :type => :GvDouble },
|
198
|
+
"margin" => { :usedBy => "NG", :type => :EscString }, # GvDouble , pointf
|
196
199
|
"maxiter" => { :usedBy => "G", :type => :EscString },
|
197
|
-
"mclimit" => { :usedBy => "G", :type => :
|
198
|
-
"mindist" => { :usedBy => "G", :type => :
|
200
|
+
"mclimit" => { :usedBy => "G", :type => :GvDouble },
|
201
|
+
"mindist" => { :usedBy => "G", :type => :GvDouble },
|
199
202
|
"minlen" => { :usedBy => "E", :type => :EscString },
|
200
203
|
"mode" => { :usedBy => "G", :type => :EscString },
|
201
204
|
"model" => { :usedBy => "G", :type => :EscString },
|
202
205
|
"mosek" => { :usedBy => "G", :type => :EscString },
|
203
|
-
"nodesep" => { :usedBy => "G", :type => :
|
206
|
+
"nodesep" => { :usedBy => "G", :type => :GvDouble },
|
204
207
|
"nojustify" => { :usedBy => "GCNE", :type => :EscString },
|
205
208
|
"normalize" => { :usedBy => "G", :type => :EscString },
|
206
|
-
"nslimit" => { :usedBy => "G", :type => :
|
207
|
-
"nslimit1" => { :usedBy => "G", :type => :
|
209
|
+
"nslimit" => { :usedBy => "G", :type => :GvDouble },
|
210
|
+
"nslimit1" => { :usedBy => "G", :type => :GvDouble },
|
208
211
|
"ordering" => { :usedBy => "G", :type => :EscString },
|
209
|
-
"orientation" => { :usedBy => "NG", :type => :
|
212
|
+
"orientation" => { :usedBy => "NG", :type => :GvDouble },
|
210
213
|
"outputorder" => { :usedBy => "G", :type => :EscString },
|
211
214
|
"overlap" => { :usedBy => "G", :type => :EscString },
|
212
|
-
"overlap_scaling" => { :usedBy => "G", :type => :
|
215
|
+
"overlap_scaling" => { :usedBy => "G", :type => :GvDouble },
|
213
216
|
"pack" => { :usedBy => "G", :type => :EscString },
|
214
217
|
"packmode" => { :usedBy => "G", :type => :EscString },
|
215
|
-
"pad" => { :usedBy => "G", :type => :EscString },
|
216
|
-
"page" => { :usedBy => "G", :type => :EscString },
|
218
|
+
"pad" => { :usedBy => "G", :type => :EscString }, # GvDouble , pointf
|
219
|
+
"page" => { :usedBy => "G", :type => :EscString }, # GvDouble , pointf
|
217
220
|
"pagedir" => { :usedBy => "G", :type => :EscString },
|
218
221
|
"pencolor" => { :usedBy => "C", :type => :EscString },
|
219
|
-
"penwidth" => { :usedBy => "CNE", :type => :
|
222
|
+
"penwidth" => { :usedBy => "CNE", :type => :GvDouble },
|
220
223
|
"peripheries" => { :usedBy => "NC", :type => :EscString },
|
221
224
|
"pin" => { :usedBy => "N", :type => :EscString },
|
222
225
|
"pos" => { :usedBy => "EN", :type => :EscString },
|
223
226
|
"quadtree" => { :usedBy => "G", :type => :EscString },
|
224
|
-
"quantum" => { :usedBy => "G", :type => :
|
227
|
+
"quantum" => { :usedBy => "G", :type => :GvDouble },
|
225
228
|
"rank" => { :usedBy => "S", :type => :EscString },
|
226
229
|
"rankdir" => { :usedBy => "G", :type => :EscString },
|
227
|
-
"ranksep" => { :usedBy => "G", :type => :EscString },
|
228
|
-
"ratio" => { :usedBy => "G", :type => :EscString },
|
230
|
+
"ranksep" => { :usedBy => "G", :type => :EscString }, # GvDouble, doubleList
|
231
|
+
"ratio" => { :usedBy => "G", :type => :EscString }, # GvDouble, String
|
229
232
|
"rects" => { :usedBy => "N", :type => :EscString },
|
230
233
|
"regular" => { :usedBy => "N", :type => :EscString },
|
231
234
|
"remincross" => { :usedBy => "G", :type => :EscString },
|
232
|
-
"repulsiveforce" => { :usedBy => "G", :type => :
|
233
|
-
"resolution" => { :usedBy => "G", :type => :
|
235
|
+
"repulsiveforce" => { :usedBy => "G", :type => :GvDouble },
|
236
|
+
"resolution" => { :usedBy => "G", :type => :GvDouble },
|
234
237
|
"root" => { :usedBy => "GN", :type => :EscString },
|
235
238
|
"rotate" => { :usedBy => "G", :type => :EscString },
|
236
239
|
"samehead" => { :usedBy => "E", :type => :EscString },
|
237
240
|
"sametail" => { :usedBy => "E", :type => :EscString },
|
238
241
|
"samplepoints" => { :usedBy => "G", :type => :EscString },
|
239
242
|
"searchsize" => { :usedBy => "G", :type => :EscString },
|
240
|
-
"sep" => { :usedBy => "G", :type => :EscString },
|
243
|
+
"sep" => { :usedBy => "G", :type => :EscString }, # GvDouble , pointf
|
241
244
|
"shape" => { :usedBy => "N", :type => :EscString },
|
242
245
|
"shapefile" => { :usedBy => "N", :type => :EscString },
|
243
246
|
"showboxes" => { :usedBy => "ENG", :type => :EscString },
|
244
247
|
"sides" => { :usedBy => "N", :type => :EscString },
|
245
|
-
"size" => { :usedBy => "NG", :type => :EscString },
|
246
|
-
"skew" => { :usedBy => "N", :type => :
|
248
|
+
"size" => { :usedBy => "NG", :type => :EscString }, # GvDouble , pointf
|
249
|
+
"skew" => { :usedBy => "N", :type => :GvDouble },
|
247
250
|
"smoothing" => { :usedBy => "G", :type => :EscString },
|
248
251
|
"sortv" => { :usedBy => "GCN", :type => :EscString },
|
249
252
|
"splines" => { :usedBy => "G", :type => :EscString },
|
@@ -262,10 +265,10 @@ module Constants
|
|
262
265
|
"truecolor" => { :usedBy => "G", :type => :EscString },
|
263
266
|
"vertices" => { :usedBy => "N", :type => :EscString },
|
264
267
|
"viewport" => { :usedBy => "G", :type => :EscString },
|
265
|
-
"voro_margin" => { :usedBy => "G", :type => :
|
266
|
-
"weight" => { :usedBy => "E", :type => :
|
267
|
-
"width" => { :usedBy => "N", :type => :
|
268
|
-
"z" => { :usedBy => "N", :type => :
|
268
|
+
"voro_margin" => { :usedBy => "G", :type => :GvDouble },
|
269
|
+
"weight" => { :usedBy => "E", :type => :GvDouble },
|
270
|
+
"width" => { :usedBy => "N", :type => :GvDouble },
|
271
|
+
"z" => { :usedBy => "N", :type => :GvDouble }
|
269
272
|
}
|
270
273
|
|
271
274
|
## Const: Graph attributs
|
data/lib/graphviz/edge.rb
CHANGED
@@ -52,11 +52,13 @@ class GraphViz
|
|
52
52
|
@oGParrent = oGParrent
|
53
53
|
|
54
54
|
@oAttrEdge = GraphViz::Attrs::new( nil, "edge", EDGESATTRS )
|
55
|
+
|
56
|
+
@index = nil
|
55
57
|
end
|
56
58
|
|
57
59
|
# Return the node one as string (so with port if any)
|
58
|
-
def node_one
|
59
|
-
if @xNodeOnePort.nil?
|
60
|
+
def node_one( with_port = true )
|
61
|
+
if @xNodeOnePort.nil? or with_port == false
|
60
62
|
GraphViz.escape(@xNodeOne)
|
61
63
|
else
|
62
64
|
GraphViz.escape(@xNodeOne, true) + ":#{@xNodeOnePort}"
|
@@ -64,14 +66,24 @@ class GraphViz
|
|
64
66
|
end
|
65
67
|
|
66
68
|
# Return the node two as string (so with port if any)
|
67
|
-
def node_two
|
68
|
-
if @xNodeTwoPort.nil?
|
69
|
+
def node_two( with_port = true )
|
70
|
+
if @xNodeTwoPort.nil? or with_port == false
|
69
71
|
GraphViz.escape(@xNodeTwo)
|
70
72
|
else
|
71
73
|
GraphViz.escape(@xNodeTwo, true) + ":#{@xNodeTwoPort}"
|
72
74
|
end
|
73
75
|
end
|
74
76
|
|
77
|
+
#
|
78
|
+
# Return the index of the edge
|
79
|
+
#
|
80
|
+
def index
|
81
|
+
@index
|
82
|
+
end
|
83
|
+
def index=(i) #:nodoc:
|
84
|
+
@index = i if @index == nil
|
85
|
+
end
|
86
|
+
|
75
87
|
#
|
76
88
|
# Set value +xAttrValue+ to the edge attribut +xAttrName+
|
77
89
|
#
|
@@ -91,7 +103,11 @@ class GraphViz
|
|
91
103
|
self[key] = value
|
92
104
|
end
|
93
105
|
else
|
94
|
-
@oAttrEdge[xAttrName.to_s]
|
106
|
+
if @oAttrEdge[xAttrName.to_s]
|
107
|
+
@oAttrEdge[xAttrName.to_s].clone
|
108
|
+
else
|
109
|
+
nil
|
110
|
+
end
|
95
111
|
end
|
96
112
|
end
|
97
113
|
|
@@ -122,6 +138,7 @@ class GraphViz
|
|
122
138
|
# Add edge options
|
123
139
|
# use edge.<option>=<value> or edge.<option>( <value> )
|
124
140
|
def method_missing( idName, *args, &block ) #:nodoc:
|
141
|
+
return if idName == :to_ary # ruby 1.9.2 fix
|
125
142
|
xName = idName.id2name
|
126
143
|
|
127
144
|
self[xName.gsub( /=$/, "" )]=args[0]
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class GraphViz
|
2
|
+
class Elements
|
3
|
+
def initialize
|
4
|
+
@elements = Array.new
|
5
|
+
@elements_hash_by_type = Hash.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def push( obj )
|
9
|
+
@elements.push( obj )
|
10
|
+
if @elements_hash_by_type[obj['type']].nil?
|
11
|
+
@elements_hash_by_type[obj['type']] = Array.new
|
12
|
+
end
|
13
|
+
|
14
|
+
@elements_hash_by_type[obj['type']].push( obj )
|
15
|
+
end
|
16
|
+
|
17
|
+
def each( &b )
|
18
|
+
@elements.each do |e|
|
19
|
+
yield( e )
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def size_of( type )
|
24
|
+
if @elements_hash_by_type[type].nil?
|
25
|
+
return 0
|
26
|
+
else
|
27
|
+
return @elements_hash_by_type[type].size
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def []( index, type = nil )
|
32
|
+
if type.nil?
|
33
|
+
return @elements[index]
|
34
|
+
else
|
35
|
+
return @elements_hash_by_type[type][index]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright (C) 2010 Gregoire Lejeune <gregoire.lejeune@free.fr>
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
17
|
+
|
18
|
+
require 'graphviz'
|
19
|
+
require 'rexml/document'
|
20
|
+
|
21
|
+
class GraphViz
|
22
|
+
class GraphML
|
23
|
+
attr_reader :attributs
|
24
|
+
attr_accessor :graph
|
25
|
+
|
26
|
+
DEST = {
|
27
|
+
'node' => [:nodes],
|
28
|
+
'edge' => [:edges],
|
29
|
+
'graph' => [:graphs],
|
30
|
+
'all' => [:nodes, :edges, :graphs]
|
31
|
+
}
|
32
|
+
|
33
|
+
GTYPE = {
|
34
|
+
'directed' => :digraph,
|
35
|
+
'undirected' => :graph
|
36
|
+
}
|
37
|
+
|
38
|
+
def initialize( xFile )
|
39
|
+
@xmlDoc = REXML::Document::new( File::new( xFile ) )
|
40
|
+
@attributs = {
|
41
|
+
:nodes => {},
|
42
|
+
:edges => {},
|
43
|
+
:graphs => {}
|
44
|
+
}
|
45
|
+
@graph = nil
|
46
|
+
@current_attr = nil
|
47
|
+
@current_node = nil
|
48
|
+
@current_edge = nil
|
49
|
+
@current_graph = nil
|
50
|
+
|
51
|
+
parse( @xmlDoc.root )
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse( node )
|
55
|
+
#begin
|
56
|
+
send( node.name.to_sym, node )
|
57
|
+
#rescue NoMethodError => e
|
58
|
+
# raise "ERROR node #{node.name} can be root"
|
59
|
+
#end
|
60
|
+
end
|
61
|
+
|
62
|
+
def graphml( node )
|
63
|
+
node.each_element( ) do |child|
|
64
|
+
#begin
|
65
|
+
send( "graphml_#{child.name}".to_sym, child )
|
66
|
+
#rescue NoMethodError => e
|
67
|
+
# raise "ERROR node #{child.name} can be child of graphml"
|
68
|
+
#end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def graphml_key( node )
|
73
|
+
id = node.attributes['id']
|
74
|
+
@current_attr = {
|
75
|
+
:name => node.attributes['attr.name'],
|
76
|
+
:type => node.attributes['attr.type']
|
77
|
+
}
|
78
|
+
DEST[node.attributes['for']].each do |d|
|
79
|
+
@attributs[d][id] = @current_attr
|
80
|
+
end
|
81
|
+
|
82
|
+
node.each_element( ) do |child|
|
83
|
+
begin
|
84
|
+
send( "graphml_key_#{child.name}".to_sym, child )
|
85
|
+
rescue NoMethodError => e
|
86
|
+
raise "ERROR node #{child.name} can be child of graphml"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
@current_attr = nil
|
91
|
+
end
|
92
|
+
|
93
|
+
def graphml_key_default( node )
|
94
|
+
@current_attr[:default] = node.texts().to_s
|
95
|
+
end
|
96
|
+
|
97
|
+
def graphml_graph( node )
|
98
|
+
@current_node = nil
|
99
|
+
|
100
|
+
if @current_graph.nil?
|
101
|
+
@graph = GraphViz.new( node.attributes['id'], :type => GTYPE[node.attributes['edgedefault']] )
|
102
|
+
@current_graph = @graph
|
103
|
+
previous_graph = @graph
|
104
|
+
else
|
105
|
+
previous_graph = @current_graph
|
106
|
+
@current_graph = previous_graph.add_graph( node.attributes['id'] )
|
107
|
+
end
|
108
|
+
|
109
|
+
@attributs[:graphs].each do |id, data|
|
110
|
+
@current_graph.graph[data[:name]] = data[:default] if data.has_key?(:default)
|
111
|
+
end
|
112
|
+
@attributs[:nodes].each do |id, data|
|
113
|
+
@current_graph.node[data[:name]] = data[:default] if data.has_key?(:default)
|
114
|
+
end
|
115
|
+
@attributs[:edges].each do |id, data|
|
116
|
+
@current_graph.edge[data[:name]] = data[:default] if data.has_key?(:default)
|
117
|
+
end
|
118
|
+
|
119
|
+
node.each_element( ) do |child|
|
120
|
+
#begin
|
121
|
+
send( "graphml_graph_#{child.name}".to_sym, child )
|
122
|
+
#rescue NoMethodError => e
|
123
|
+
# raise "ERROR node #{child.name} can be child of graphml"
|
124
|
+
#end
|
125
|
+
end
|
126
|
+
|
127
|
+
@current_graph = previous_graph
|
128
|
+
end
|
129
|
+
|
130
|
+
def graphml_graph_data( node )
|
131
|
+
@current_graph[@attributs[:graphs][node.attributes['key']][:name]] = node.texts().to_s
|
132
|
+
end
|
133
|
+
|
134
|
+
def graphml_graph_node( node )
|
135
|
+
@current_node = {}
|
136
|
+
|
137
|
+
node.each_element( ) do |child|
|
138
|
+
case child.name
|
139
|
+
when "graph"
|
140
|
+
graphml_graph( child )
|
141
|
+
else
|
142
|
+
begin
|
143
|
+
send( "graphml_graph_node_#{child.name}".to_sym, child )
|
144
|
+
rescue NoMethodError => e
|
145
|
+
raise "ERROR node #{child.name} can be child of graphml"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
unless @current_node.nil?
|
151
|
+
node = @current_graph.add_node( node.attributes['id'] )
|
152
|
+
@current_node.each do |k, v|
|
153
|
+
node[k] = v
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
@current_node = nil
|
158
|
+
end
|
159
|
+
|
160
|
+
def graphml_graph_node_data( node )
|
161
|
+
@current_node[@attributs[:nodes][node.attributes['key']][:name]] = node.texts().to_s
|
162
|
+
end
|
163
|
+
|
164
|
+
def graphml_graph_node_port( node )
|
165
|
+
@current_node[:shape] = :record
|
166
|
+
port = node.attributes['name']
|
167
|
+
if @current_node[:label]
|
168
|
+
label = @current_node[:label].gsub( "{", "" ).gsub( "}", "" )
|
169
|
+
@current_node[:label] = "#{label}|<#{port}> #{port}"
|
170
|
+
else
|
171
|
+
@current_node[:label] = "<#{port}> #{port}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def graphml_graph_edge( node )
|
176
|
+
source = node.attributes['source']
|
177
|
+
source = {source => node.attributes['sourceport']} if node.attributes['sourceport']
|
178
|
+
target = node.attributes['target']
|
179
|
+
target = {target => node.attributes['targetport']} if node.attributes['targetport']
|
180
|
+
|
181
|
+
@current_edge = @current_graph.add_edge( source, target )
|
182
|
+
|
183
|
+
node.each_element( ) do |child|
|
184
|
+
#begin
|
185
|
+
send( "graphml_graph_edge_#{child.name}".to_sym, child )
|
186
|
+
#rescue NoMethodError => e
|
187
|
+
# raise "ERROR node #{child.name} can be child of graphml"
|
188
|
+
#end
|
189
|
+
end
|
190
|
+
|
191
|
+
@current_edge = nil
|
192
|
+
end
|
193
|
+
|
194
|
+
def graphml_graph_edge_data( node )
|
195
|
+
@current_edge[@attributs[:edges][node.attributes['key']][:name]] = node.texts().to_s
|
196
|
+
end
|
197
|
+
|
198
|
+
def graphml_graph_hyperedge( node )
|
199
|
+
list = []
|
200
|
+
|
201
|
+
node.each_element( ) do |child|
|
202
|
+
if child.name == "endpoint"
|
203
|
+
if child.attributes['port']
|
204
|
+
list << { child.attributes['node'] => child.attributes['port'] }
|
205
|
+
else
|
206
|
+
list << child.attributes['node']
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
list.each { |s|
|
212
|
+
list.each { |t|
|
213
|
+
@current_graph.add_edge( s, t ) unless s == t
|
214
|
+
}
|
215
|
+
}
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|