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,129 @@
|
|
1
|
+
# Copyright (C) 2004, 2005, 2006, 2007, 2008 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 'graphviz/attrs'
|
18
|
+
require 'graphviz/constants'
|
19
|
+
|
20
|
+
class GraphViz
|
21
|
+
class Node
|
22
|
+
include Constants
|
23
|
+
@xNodeName
|
24
|
+
@oAttrNode
|
25
|
+
@oGParrent
|
26
|
+
|
27
|
+
#
|
28
|
+
# Create a new node
|
29
|
+
#
|
30
|
+
# In:
|
31
|
+
# * xNodeName : Name of the node
|
32
|
+
# * oGParrent : Graph
|
33
|
+
#
|
34
|
+
def initialize( xNodeName, oGParrent = nil )
|
35
|
+
@xNodeName = xNodeName
|
36
|
+
@oGParrent = oGParrent
|
37
|
+
@oAttrNode = GraphViz::Attrs::new( nil, "node", NODESATTRS )
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Get the node name
|
42
|
+
#
|
43
|
+
def name
|
44
|
+
@xNodeName.clone
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Set value +xAttrValue+ to the node attribut +xAttrName+
|
49
|
+
#
|
50
|
+
def []=( xAttrName, xAttrValue )
|
51
|
+
xAttrValue = xAttrValue.to_s if xAttrValue.class == Symbol
|
52
|
+
@oAttrNode[xAttrName.to_s] = xAttrValue
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Get the value of the node attribut +xAttrName+
|
57
|
+
#
|
58
|
+
def []( xAttrName )
|
59
|
+
@oAttrNode[xAttrName.to_s].clone
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Create an edge between the current node and the node +oNode+
|
64
|
+
#
|
65
|
+
def <<( oNode )
|
66
|
+
if( oNode.class == Array )
|
67
|
+
oNode.each do |no|
|
68
|
+
self << no
|
69
|
+
end
|
70
|
+
else
|
71
|
+
return GraphViz::commonGraph( oNode, self ).add_edge( self, oNode )
|
72
|
+
end
|
73
|
+
end
|
74
|
+
alias :> :<<
|
75
|
+
alias :- :<<
|
76
|
+
alias :>> :<<
|
77
|
+
|
78
|
+
#
|
79
|
+
# Set node attributs
|
80
|
+
#
|
81
|
+
# Example :
|
82
|
+
# n = graph.add_node( ... )
|
83
|
+
# ...
|
84
|
+
# n.set { |_n|
|
85
|
+
# _n.color = "blue"
|
86
|
+
# _n.fontcolor = "red"
|
87
|
+
# }
|
88
|
+
#
|
89
|
+
def set( &b )
|
90
|
+
yield( self )
|
91
|
+
end
|
92
|
+
|
93
|
+
# Add node options
|
94
|
+
# use node.<option>=<value> or node.<option>( <value> )
|
95
|
+
def method_missing( idName, *args, &block ) #:nodoc:
|
96
|
+
xName = idName.id2name
|
97
|
+
|
98
|
+
self[xName.gsub( /=$/, "" )]=args[0]
|
99
|
+
end
|
100
|
+
|
101
|
+
def pg #:nodoc:
|
102
|
+
@oGParrent
|
103
|
+
end
|
104
|
+
|
105
|
+
def output #:nodoc:
|
106
|
+
#xNodeName = @xNodeName.clone
|
107
|
+
#xNodeName = '"' << xNodeName << '"' if xNodeName.match( /^[a-zA-Z_]+[a-zA-Z0-9_\.]*$/ ).nil?
|
108
|
+
xNodeName = GraphViz.escape(@xNodeName)
|
109
|
+
|
110
|
+
xOut = "" << xNodeName
|
111
|
+
xAttr = ""
|
112
|
+
xSeparator = ""
|
113
|
+
@oAttrNode.data.each do |k, v|
|
114
|
+
if k == "html"
|
115
|
+
xAttr << xSeparator + "label = <" + v + ">"
|
116
|
+
else
|
117
|
+
xAttr << xSeparator + k + " = " + GraphViz.escape(v, true)
|
118
|
+
end
|
119
|
+
xSeparator = ", "
|
120
|
+
end
|
121
|
+
if xAttr.length > 0
|
122
|
+
xOut << " [" + xAttr + "]"
|
123
|
+
end
|
124
|
+
xOut << ";"
|
125
|
+
|
126
|
+
return( xOut )
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'treetop'
|
3
|
+
|
4
|
+
Treetop.load File.dirname(__FILE__) + '/dot.treetop'
|
5
|
+
|
6
|
+
class GraphViz
|
7
|
+
class Parser
|
8
|
+
|
9
|
+
class Context
|
10
|
+
def initialize
|
11
|
+
@graph = nil
|
12
|
+
@nodes = {}
|
13
|
+
@edges = []
|
14
|
+
@options = {
|
15
|
+
:node => {},
|
16
|
+
:edge => {}
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def graph
|
21
|
+
@graph
|
22
|
+
end
|
23
|
+
|
24
|
+
def graph=(g)
|
25
|
+
@graph = g
|
26
|
+
end
|
27
|
+
|
28
|
+
def nodes
|
29
|
+
@nodes
|
30
|
+
end
|
31
|
+
|
32
|
+
def edges
|
33
|
+
@edges
|
34
|
+
end
|
35
|
+
|
36
|
+
def options
|
37
|
+
@options
|
38
|
+
end
|
39
|
+
|
40
|
+
def options=(o)
|
41
|
+
@options = o
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Graph < Treetop::Runtime::SyntaxNode
|
46
|
+
def eval( context, hOpts )
|
47
|
+
# puts "GRAPH TYPE = #{type.text_value}"
|
48
|
+
# puts "GRAPH NAME = #{name.text_value}"
|
49
|
+
|
50
|
+
hOpts = hOpts[0].merge( {:type => type.text_value} )
|
51
|
+
|
52
|
+
# Create Graph
|
53
|
+
context.graph = GraphViz.new( name.text_value.gsub(/"/, ""), hOpts )
|
54
|
+
|
55
|
+
# Eval cluster
|
56
|
+
cluster.eval( context )
|
57
|
+
|
58
|
+
return context.graph
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Cluster < Treetop::Runtime::SyntaxNode
|
63
|
+
def eval( context )
|
64
|
+
content.elements.each do |e|
|
65
|
+
e.eval( context )
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class GraphPreference < Treetop::Runtime::SyntaxNode
|
71
|
+
def eval( context )
|
72
|
+
# puts "GRAPH PREFERENCE : "
|
73
|
+
# puts " #{key.text_value} = #{value.text_value.gsub(/"/, "")}"
|
74
|
+
context.graph[key.text_value] = value.text_value.gsub(/"/, "")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class NamedGraphPreference < Treetop::Runtime::SyntaxNode
|
79
|
+
def eval( context )
|
80
|
+
# puts "GRAPH PREFERENCES :"
|
81
|
+
options.eval().each do |k,v|
|
82
|
+
context.graph[k] = v
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class NodePreference < Treetop::Runtime::SyntaxNode
|
88
|
+
def eval( context )
|
89
|
+
# puts "NODE PREFERENCES :"
|
90
|
+
context.options[:node] = context.options[:node].merge( options.eval() )
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class EdgePreference < Treetop::Runtime::SyntaxNode
|
95
|
+
def eval( context )
|
96
|
+
# puts "EDGE PREFERENCES :"
|
97
|
+
context.options[:edge] = context.options[:edge].merge( options.eval() )
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Node < Treetop::Runtime::SyntaxNode
|
102
|
+
def eval( context )
|
103
|
+
node_name = name.text_value.gsub( /"/, "" )
|
104
|
+
# puts "NODE NAME = #{node_name}"
|
105
|
+
# puts "OPTIONS = "
|
106
|
+
|
107
|
+
# Create node
|
108
|
+
node = context.nodes[node_name] || context.graph.add_node( node_name )
|
109
|
+
|
110
|
+
# Add global options
|
111
|
+
context.options[:node].each do |k, v|
|
112
|
+
node[k] = v
|
113
|
+
end
|
114
|
+
|
115
|
+
# Add custom options
|
116
|
+
unless options.terminal?
|
117
|
+
options.eval().each do |k, v|
|
118
|
+
node[k] = v
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Save node
|
123
|
+
context.nodes[node_name] = node
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class Edge < Treetop::Runtime::SyntaxNode
|
128
|
+
def create_node( name, context )
|
129
|
+
# puts " NEED TO CREATE NODE : #{name}"
|
130
|
+
# Create the node
|
131
|
+
node = context.graph.add_node( name )
|
132
|
+
|
133
|
+
# Add global options
|
134
|
+
context.options[:node].each do |k, v|
|
135
|
+
node[k] = v
|
136
|
+
end
|
137
|
+
|
138
|
+
# Save node
|
139
|
+
context.nodes[name] = node
|
140
|
+
end
|
141
|
+
|
142
|
+
def create_edge( one, two, edge_options, context )
|
143
|
+
# Create edge
|
144
|
+
edge = context.graph.add_edge( one, two )
|
145
|
+
|
146
|
+
# Add global options
|
147
|
+
context.options[:edge].each do |k, v|
|
148
|
+
edge[k] = v
|
149
|
+
end
|
150
|
+
|
151
|
+
# Add custom options
|
152
|
+
edge_options.each do |k, v|
|
153
|
+
edge[k] = v
|
154
|
+
end
|
155
|
+
|
156
|
+
# Save edge
|
157
|
+
context.edges << edge
|
158
|
+
end
|
159
|
+
|
160
|
+
def eval( context )
|
161
|
+
one_name = node_one.text_value.gsub( /"/, "" )
|
162
|
+
two_name = node_two.text_value.gsub( /"/, "" )
|
163
|
+
# puts "EDGE"
|
164
|
+
# puts "NODE ONE = #{one_name}"
|
165
|
+
# puts "NODE TWO = #{two_name}"
|
166
|
+
# puts "OPTIONS = "
|
167
|
+
|
168
|
+
# Get or create node one
|
169
|
+
one = context.nodes[one_name] || create_node( one_name, context )
|
170
|
+
|
171
|
+
# Get or create node two
|
172
|
+
two = context.nodes[two_name] || create_node( two_name, context )
|
173
|
+
|
174
|
+
# Get options
|
175
|
+
edge_options = {}
|
176
|
+
edge_options = options.eval() unless options.terminal?
|
177
|
+
|
178
|
+
# Create edge
|
179
|
+
create_edge( one, two, edge_options, context )
|
180
|
+
|
181
|
+
last_node = two
|
182
|
+
other_nodes.elements.each do |e|
|
183
|
+
new_node_name = e.next_node.text_value.gsub( /"/, "" )
|
184
|
+
# puts "OTHER NODE : #{new_node_name}"
|
185
|
+
|
186
|
+
new_node = context.nodes[new_node_name] || create_node( new_node_name, context )
|
187
|
+
create_edge( last_node, new_node, edge_options, context )
|
188
|
+
|
189
|
+
last_node = new_node
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
class Subgraph < Treetop::Runtime::SyntaxNode
|
195
|
+
def eval( context )
|
196
|
+
# puts "CREATE SUBGRAPH : #{name.text_value}"
|
197
|
+
|
198
|
+
# Save options
|
199
|
+
saved_options = context.options.clone
|
200
|
+
# Save graph
|
201
|
+
saved_graph = context.graph
|
202
|
+
|
203
|
+
# Create Graph
|
204
|
+
context.graph = context.graph.add_graph( name.text_value.gsub(/"/, "") )
|
205
|
+
#context.options = {
|
206
|
+
# :node => {},
|
207
|
+
# :edge => {}
|
208
|
+
#}
|
209
|
+
|
210
|
+
# Eval cluster
|
211
|
+
cluster.eval( context )
|
212
|
+
|
213
|
+
# Reinitialize graph and options
|
214
|
+
context.graph = saved_graph
|
215
|
+
context.options = saved_options
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
class Options < Treetop::Runtime::SyntaxNode
|
220
|
+
def eval
|
221
|
+
options = {}
|
222
|
+
elements[2].elements.each do |e|
|
223
|
+
# puts " #{e.elements[0].text_value} = #{e.elements[4].text_value}"
|
224
|
+
options[e.elements[0].text_value] = e.elements[4].text_value.gsub( /"/, "" )
|
225
|
+
end
|
226
|
+
# puts " #{elements[3].text_value} = #{elements[7].text_value}"
|
227
|
+
options[elements[3].text_value] = elements[7].text_value.gsub( /"/, "" )
|
228
|
+
|
229
|
+
return options
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
class Rank < Treetop::Runtime::SyntaxNode
|
234
|
+
def eval( context )
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.parse( file, *hOpts, &block )
|
239
|
+
dot = open(file).read
|
240
|
+
parser = DotParser.new()
|
241
|
+
tree = parser.parse( dot )
|
242
|
+
graph = tree.eval( GraphViz::Parser::Context.new(), hOpts )
|
243
|
+
|
244
|
+
yield( graph ) if( block and graph.nil? == false )
|
245
|
+
|
246
|
+
return graph
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
data/lib/graphviz/xml.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# Copyright (C) 2004, 2005, 2006, 2007, 2008 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 'graphviz'
|
18
|
+
require 'rexml/document'
|
19
|
+
|
20
|
+
class GraphViz
|
21
|
+
class XML
|
22
|
+
|
23
|
+
@oReXML
|
24
|
+
@oGraph
|
25
|
+
@xNodeName
|
26
|
+
@bShowText
|
27
|
+
@bShowAttrs
|
28
|
+
|
29
|
+
#
|
30
|
+
# Generate the graph
|
31
|
+
#
|
32
|
+
# Options :
|
33
|
+
# * :output : Output format (Constants::FORMATS)
|
34
|
+
# * :file : Output file name
|
35
|
+
# * :use : Program to use (Constants::PROGRAMS)
|
36
|
+
# * :path : Program PATH
|
37
|
+
# * :<format> => <file> : <file> can be
|
38
|
+
# * a file name
|
39
|
+
# * nil, then the output will be printed to STDOUT
|
40
|
+
# * String, then the output will be returned as a String
|
41
|
+
# * :errors : DOT error level (default 1)
|
42
|
+
# * 0 = Error + Warning
|
43
|
+
# * 1 = Error
|
44
|
+
# * 2 = none
|
45
|
+
#
|
46
|
+
def output( *hOpt )
|
47
|
+
@oGraph.output( *hOpt )
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
#
|
53
|
+
# Create a graph from a XML file
|
54
|
+
#
|
55
|
+
# In:
|
56
|
+
# * xFile : XML File
|
57
|
+
# * *hOpt : Graph options
|
58
|
+
#
|
59
|
+
def initialize( xFile, *hOpt )
|
60
|
+
@xNodeName = "00000"
|
61
|
+
@bShowText = true
|
62
|
+
@bShowAttrs = true
|
63
|
+
|
64
|
+
if hOpt.nil? == false and hOpt[0].nil? == false
|
65
|
+
hOpt[0].each do |xKey, xValue|
|
66
|
+
case xKey.to_s
|
67
|
+
when "text"
|
68
|
+
@bShowText = xValue
|
69
|
+
hOpt[0].delete( xKey )
|
70
|
+
when "attrs"
|
71
|
+
@bShowAttrs = xValue
|
72
|
+
hOpt[0].delete( xKey )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
@oReXML = REXML::Document::new( File::new( xFile ) )
|
78
|
+
@oGraph = GraphViz::new( "XML", *hOpt )
|
79
|
+
_init( @oReXML.root() )
|
80
|
+
end
|
81
|
+
|
82
|
+
def _init( oXMLNode ) #:nodoc:
|
83
|
+
xLocalNodeName = @xNodeName.clone
|
84
|
+
@xNodeName.succ!
|
85
|
+
|
86
|
+
label = oXMLNode.name
|
87
|
+
if oXMLNode.has_attributes? == true and @bShowAttrs == true
|
88
|
+
label = "{ " + oXMLNode.name
|
89
|
+
|
90
|
+
oXMLNode.attributes.each do |xName, xValue|
|
91
|
+
label << "| { #{xName} | #{xValue} } "
|
92
|
+
end
|
93
|
+
|
94
|
+
label << "}"
|
95
|
+
end
|
96
|
+
@oGraph.add_node( xLocalNodeName, "label" => label, "color" => "blue", "shape" => "record" )
|
97
|
+
|
98
|
+
## Act: Search and add Text nodes
|
99
|
+
if oXMLNode.has_text? == true and @bShowText == true
|
100
|
+
xTextNodeName = xLocalNodeName.clone
|
101
|
+
xTextNodeName << "111"
|
102
|
+
|
103
|
+
xText = ""
|
104
|
+
xSep = ""
|
105
|
+
oXMLNode.texts().each do |l|
|
106
|
+
x = l.value.chomp.strip
|
107
|
+
if x.length > 0
|
108
|
+
xText << xSep << x
|
109
|
+
xSep = "\n"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
if xText.length > 0
|
114
|
+
@oGraph.add_node( xTextNodeName, "label" => xText, "color" => "black", "shape" => "ellipse" )
|
115
|
+
@oGraph.add_edge( xLocalNodeName, xTextNodeName )
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
## Act: Search and add attributs
|
120
|
+
## TODO
|
121
|
+
|
122
|
+
oXMLNode.each_element( ) do |oXMLChild|
|
123
|
+
xChildNodeName = _init( oXMLChild )
|
124
|
+
@oGraph.add_edge( xLocalNodeName, xChildNodeName )
|
125
|
+
end
|
126
|
+
|
127
|
+
return( xLocalNodeName )
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|