rdoc-f95 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +79 -0
  3. data/PostInstall.txt +7 -0
  4. data/README.rdoc +147 -0
  5. data/Rakefile +28 -0
  6. data/bin/rdoc-f95 +70 -0
  7. data/lib/rdoc-f95.rb +306 -0
  8. data/lib/rdoc-f95/code_objects.rb +776 -0
  9. data/lib/rdoc-f95/diagram.rb +342 -0
  10. data/lib/rdoc-f95/dot.rb +249 -0
  11. data/lib/rdoc-f95/generator.rb +1088 -0
  12. data/lib/rdoc-f95/generator/chm.rb +113 -0
  13. data/lib/rdoc-f95/generator/chm/chm.rb +98 -0
  14. data/lib/rdoc-f95/generator/html.rb +370 -0
  15. data/lib/rdoc-f95/generator/html/hefss.rb +414 -0
  16. data/lib/rdoc-f95/generator/html/html.rb +708 -0
  17. data/lib/rdoc-f95/generator/html/kilmer.rb +418 -0
  18. data/lib/rdoc-f95/generator/html/one_page_html.rb +121 -0
  19. data/lib/rdoc-f95/generator/ri.rb +229 -0
  20. data/lib/rdoc-f95/generator/xhtml.rb +106 -0
  21. data/lib/rdoc-f95/generator/xhtml/ctop.xsl +1318 -0
  22. data/lib/rdoc-f95/generator/xhtml/mathml.xsl +42 -0
  23. data/lib/rdoc-f95/generator/xhtml/pmathml.xsl +612 -0
  24. data/lib/rdoc-f95/generator/xhtml/pmathmlcss.xsl +872 -0
  25. data/lib/rdoc-f95/generator/xhtml/xhtml.rb +732 -0
  26. data/lib/rdoc-f95/generator/xml.rb +120 -0
  27. data/lib/rdoc-f95/generator/xml/rdf.rb +113 -0
  28. data/lib/rdoc-f95/generator/xml/xml.rb +111 -0
  29. data/lib/rdoc-f95/install.rb +166 -0
  30. data/lib/rdoc-f95/markup.rb +506 -0
  31. data/lib/rdoc-f95/markup/formatter.rb +14 -0
  32. data/lib/rdoc-f95/markup/fragments.rb +337 -0
  33. data/lib/rdoc-f95/markup/inline.rb +361 -0
  34. data/lib/rdoc-f95/markup/install.rb +57 -0
  35. data/lib/rdoc-f95/markup/lines.rb +152 -0
  36. data/lib/rdoc-f95/markup/mathml_wrapper.rb +91 -0
  37. data/lib/rdoc-f95/markup/preprocess.rb +71 -0
  38. data/lib/rdoc-f95/markup/sample/rdoc2latex.rb +16 -0
  39. data/lib/rdoc-f95/markup/sample/sample.rb +42 -0
  40. data/lib/rdoc-f95/markup/to_flow.rb +185 -0
  41. data/lib/rdoc-f95/markup/to_html.rb +357 -0
  42. data/lib/rdoc-f95/markup/to_html_crossref.rb +123 -0
  43. data/lib/rdoc-f95/markup/to_latex.rb +328 -0
  44. data/lib/rdoc-f95/markup/to_test.rb +50 -0
  45. data/lib/rdoc-f95/markup/to_xhtml_texparser.rb +234 -0
  46. data/lib/rdoc-f95/options.rb +745 -0
  47. data/lib/rdoc-f95/parsers/parse_c.rb +775 -0
  48. data/lib/rdoc-f95/parsers/parse_f95.rb +2499 -0
  49. data/lib/rdoc-f95/parsers/parse_rb.rb +2587 -0
  50. data/lib/rdoc-f95/parsers/parse_simple.rb +39 -0
  51. data/lib/rdoc-f95/parsers/parserfactory.rb +99 -0
  52. data/lib/rdoc-f95/ri.rb +2 -0
  53. data/lib/rdoc-f95/ri/cache.rb +188 -0
  54. data/lib/rdoc-f95/ri/descriptions.rb +147 -0
  55. data/lib/rdoc-f95/ri/display.rb +244 -0
  56. data/lib/rdoc-f95/ri/driver.rb +435 -0
  57. data/lib/rdoc-f95/ri/formatter.rb +603 -0
  58. data/lib/rdoc-f95/ri/paths.rb +105 -0
  59. data/lib/rdoc-f95/ri/reader.rb +106 -0
  60. data/lib/rdoc-f95/ri/util.rb +81 -0
  61. data/lib/rdoc-f95/ri/writer.rb +64 -0
  62. data/lib/rdoc-f95/stats.rb +23 -0
  63. data/lib/rdoc-f95/template.rb +64 -0
  64. data/lib/rdoc-f95/tokenstream.rb +33 -0
  65. data/lib/rdoc-f95/usage.rb +210 -0
  66. data/script/console +10 -0
  67. data/script/destroy +14 -0
  68. data/script/generate +14 -0
  69. data/test/test_helper.rb +3 -0
  70. data/test/test_rdoc-f95.rb +11 -0
  71. metadata +156 -0
@@ -0,0 +1,342 @@
1
+ # A wonderful hack by to draw package diagrams using the dot package.
2
+ # Originally written by Jah, team Enticla.
3
+ #
4
+ # You must have the V1.7 or later in your path
5
+ # http://www.research.att.com/sw/tools/graphviz/
6
+
7
+ require 'rdoc-f95/dot'
8
+
9
+ module RDocF95
10
+
11
+ ##
12
+ # Draw a set of diagrams representing the modules and classes in the
13
+ # system. We draw one diagram for each file, and one for each toplevel
14
+ # class or module. This means there will be overlap. However, it also
15
+ # means that you'll get better context for objects.
16
+ #
17
+ # To use, simply
18
+ #
19
+ # d = Diagram.new(info) # pass in collection of top level infos
20
+ # d.draw
21
+ #
22
+ # The results will be written to the +dot+ subdirectory. The process
23
+ # also sets the +diagram+ attribute in each object it graphs to
24
+ # the name of the file containing the image. This can be used
25
+ # by output generators to insert images.
26
+
27
+ class Diagram
28
+
29
+ FONT = "Arial"
30
+
31
+ DOT_PATH = "dot"
32
+
33
+ ##
34
+ # Pass in the set of top level objects. The method also creates the
35
+ # subdirectory to hold the images
36
+
37
+ def initialize(info, options)
38
+ @info = info
39
+ @options = options
40
+ @counter = 0
41
+ FileUtils.mkdir_p(DOT_PATH)
42
+ @diagram_cache = {}
43
+ @html_suffix = ".html"
44
+ if @options.mathml
45
+ @html_suffix = ".xhtml"
46
+ end
47
+ end
48
+
49
+ ##
50
+ # Draw the diagrams. We traverse the files, drawing a diagram for each. We
51
+ # also traverse each top-level class and module in that file drawing a
52
+ # diagram for these too.
53
+
54
+ def draw
55
+ unless @options.quiet
56
+ $stderr.print "Diagrams: "
57
+ $stderr.flush
58
+ end
59
+
60
+ @info.each_with_index do |i, file_count|
61
+ @done_modules = {}
62
+ @local_names = find_names(i)
63
+ @global_names = []
64
+ @global_graph = graph = DOT::Digraph.new('name' => 'TopLevel',
65
+ 'fontname' => FONT,
66
+ 'fontsize' => '8',
67
+ 'bgcolor' => 'lightcyan1',
68
+ 'compound' => 'true')
69
+
70
+ # it's a little hack %) i'm too lazy to create a separate class
71
+ # for default node
72
+ graph << DOT::Node.new('name' => 'node',
73
+ 'fontname' => FONT,
74
+ 'color' => 'black',
75
+ 'fontsize' => 8)
76
+
77
+ i.modules.each do |mod|
78
+ draw_module(mod, graph, true, i.file_relative_name)
79
+ end
80
+ add_classes(i, graph, i.file_relative_name)
81
+
82
+ i.diagram = convert_to_png("f_#{file_count}", graph)
83
+
84
+ # now go through and document each top level class and
85
+ # module independently
86
+ i.modules.each_with_index do |mod, count|
87
+ @done_modules = {}
88
+ @local_names = find_names(mod)
89
+ @global_names = []
90
+
91
+ @global_graph = graph = DOT::Digraph.new('name' => 'TopLevel',
92
+ 'fontname' => FONT,
93
+ 'fontsize' => '8',
94
+ 'bgcolor' => 'lightcyan1',
95
+ 'compound' => 'true')
96
+
97
+ graph << DOT::Node.new('name' => 'node',
98
+ 'fontname' => FONT,
99
+ 'color' => 'black',
100
+ 'fontsize' => 8)
101
+ draw_module(mod, graph, true)
102
+ mod.diagram = convert_to_png("m_#{file_count}_#{count}",
103
+ graph)
104
+ end
105
+ end
106
+ $stderr.puts unless @options.quiet
107
+ end
108
+
109
+ private
110
+
111
+ def find_names(mod)
112
+ return [mod.full_name] + mod.classes.collect{|cl| cl.full_name} +
113
+ mod.modules.collect{|m| find_names(m)}.flatten
114
+ end
115
+
116
+ def find_full_name(name, mod)
117
+ full_name = name.dup
118
+ return full_name if @local_names.include?(full_name)
119
+ mod_path = mod.full_name.split('::')[0..-2]
120
+ unless mod_path.nil?
121
+ until mod_path.empty?
122
+ full_name = mod_path.pop + '::' + full_name
123
+ return full_name if @local_names.include?(full_name)
124
+ end
125
+ end
126
+ return name
127
+ end
128
+
129
+ def draw_module(mod, graph, toplevel = false, file = nil)
130
+ return if @done_modules[mod.full_name] and not toplevel
131
+
132
+ @counter += 1
133
+ url = mod.http_url("classes").sub(/\.html$/, @html_suffix)
134
+ m = DOT::Subgraph.new('name' => "cluster_#{mod.full_name.gsub( /:/,'_' )}",
135
+ 'label' => mod.name,
136
+ 'fontname' => FONT,
137
+ 'color' => 'blue',
138
+ 'style' => 'filled',
139
+ 'URL' => %{"#{url}"},
140
+ 'fillcolor' => toplevel ? 'palegreen1' : 'palegreen3')
141
+
142
+ @done_modules[mod.full_name] = m
143
+ add_classes(mod, m, file)
144
+ graph << m
145
+
146
+ unless mod.includes.empty?
147
+ mod.includes.each do |inc|
148
+ m_full_name = find_full_name(inc.name, mod)
149
+ if @local_names.include?(m_full_name)
150
+ @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
151
+ 'to' => "#{mod.full_name.gsub( /:/,'_' )}",
152
+ 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}",
153
+ 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
154
+ else
155
+ unless @global_names.include?(m_full_name)
156
+ path = m_full_name.split("::")
157
+ url = File.join('classes', *path) + @html_suffix
158
+ @global_graph << DOT::Node.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
159
+ 'shape' => 'box',
160
+ 'label' => "#{m_full_name}",
161
+ 'URL' => %{"#{url}"})
162
+ @global_names << m_full_name
163
+ end
164
+ @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
165
+ 'to' => "#{mod.full_name.gsub( /:/,'_' )}",
166
+ 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ def add_classes(container, graph, file = nil )
173
+
174
+ use_fileboxes = @options.fileboxes
175
+
176
+ files = {}
177
+
178
+ # create dummy node (needed if empty and for module includes)
179
+ if container.full_name
180
+ graph << DOT::Node.new('name' => "#{container.full_name.gsub( /:/,'_' )}",
181
+ 'label' => "",
182
+ 'width' => (container.classes.empty? and
183
+ container.modules.empty?) ?
184
+ '0.75' : '0.01',
185
+ 'height' => '0.01',
186
+ 'shape' => 'plaintext')
187
+ end
188
+
189
+ container.classes.each_with_index do |cl, cl_index|
190
+ last_file = cl.in_files[-1].file_relative_name
191
+
192
+ if use_fileboxes && !files.include?(last_file)
193
+ @counter += 1
194
+ files[last_file] =
195
+ DOT::Subgraph.new('name' => "cluster_#{@counter}",
196
+ 'label' => "#{last_file}",
197
+ 'fontname' => FONT,
198
+ 'color'=>
199
+ last_file == file ? 'red' : 'black')
200
+ end
201
+
202
+ next if cl.name == 'Object' || cl.name[0,2] == "<<"
203
+
204
+ url = cl.http_url("classes").sub(/\.html$/, @html_suffix)
205
+
206
+ label = cl.name.dup
207
+ if use_fileboxes && cl.in_files.length > 1
208
+ label << '\n[' +
209
+ cl.in_files.collect {|i|
210
+ i.file_relative_name
211
+ }.sort.join( '\n' ) +
212
+ ']'
213
+ end
214
+
215
+ attrs = {
216
+ 'name' => "#{cl.full_name.gsub( /:/, '_' )}",
217
+ 'fontcolor' => 'black',
218
+ 'style'=>'filled',
219
+ 'color'=>'palegoldenrod',
220
+ 'label' => label,
221
+ 'shape' => 'ellipse',
222
+ 'URL' => %{"#{url}"}
223
+ }
224
+
225
+ c = DOT::Node.new(attrs)
226
+
227
+ if use_fileboxes
228
+ files[last_file].push c
229
+ else
230
+ graph << c
231
+ end
232
+ end
233
+
234
+ if use_fileboxes
235
+ files.each_value do |val|
236
+ graph << val
237
+ end
238
+ end
239
+
240
+ unless container.classes.empty?
241
+ container.classes.each_with_index do |cl, cl_index|
242
+ cl.includes.each do |m|
243
+ m_full_name = find_full_name(m.name, cl)
244
+ if @local_names.include?(m_full_name)
245
+ @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
246
+ 'to' => "#{cl.full_name.gsub( /:/,'_' )}",
247
+ 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}")
248
+ else
249
+ unless @global_names.include?(m_full_name)
250
+ path = m_full_name.split("::")
251
+ url = File.join('classes', *path) + @html_suffix
252
+ @global_graph << DOT::Node.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
253
+ 'shape' => 'box',
254
+ 'label' => "#{m_full_name}",
255
+ 'URL' => %{"#{url}"})
256
+ @global_names << m_full_name
257
+ end
258
+ @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
259
+ 'to' => "#{cl.full_name.gsub( /:/, '_')}")
260
+ end
261
+ end
262
+
263
+ sclass = cl.superclass
264
+ next if sclass.nil? || sclass == 'Object'
265
+ sclass_full_name = find_full_name(sclass,cl)
266
+ unless @local_names.include?(sclass_full_name) or @global_names.include?(sclass_full_name)
267
+ path = sclass_full_name.split("::")
268
+ url = File.join('classes', *path) + @html_suffix
269
+ @global_graph << DOT::Node.new('name' => "#{sclass_full_name.gsub( /:/, '_' )}",
270
+ 'label' => sclass_full_name,
271
+ 'URL' => %{"#{url}"})
272
+ @global_names << sclass_full_name
273
+ end
274
+ @global_graph << DOT::Edge.new('from' => "#{sclass_full_name.gsub( /:/,'_' )}",
275
+ 'to' => "#{cl.full_name.gsub( /:/, '_')}")
276
+ end
277
+ end
278
+
279
+ container.modules.each do |submod|
280
+ draw_module(submod, graph)
281
+ end
282
+
283
+ end
284
+
285
+ def convert_to_png(file_base, graph)
286
+ str = graph.to_s
287
+ return @diagram_cache[str] if @diagram_cache[str]
288
+ op_type = @options.image_format
289
+ dotfile = File.join(DOT_PATH, file_base)
290
+ src = dotfile + ".dot"
291
+ dot = dotfile + "." + op_type
292
+
293
+ unless @options.quiet
294
+ $stderr.print "."
295
+ $stderr.flush
296
+ end
297
+
298
+ File.open(src, 'w+' ) do |f|
299
+ f << str << "\n"
300
+ end
301
+
302
+ system "dot", "-T#{op_type}", src, "-o", dot
303
+
304
+ # Now construct the imagemap wrapper around
305
+ # that png
306
+
307
+ ret = wrap_in_image_map(src, dot)
308
+ @diagram_cache[str] = ret
309
+ return ret
310
+ end
311
+
312
+ ##
313
+ # Extract the client-side image map from dot, and use it to generate the
314
+ # imagemap proper. Return the whole <map>..<img> combination, suitable for
315
+ # inclusion on the page
316
+
317
+ def wrap_in_image_map(src, dot)
318
+ res = %{<map id="map" name="map">\n}
319
+ dot_map = `dot -Tismap #{src}`
320
+ dot_map.split($/).each do |area|
321
+ unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/
322
+ $stderr.puts "Unexpected output from dot:\n#{area}"
323
+ return nil
324
+ end
325
+
326
+ xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i]
327
+ url, area_name = $5, $6
328
+
329
+ res << %{ <area shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" }
330
+ res << %{ href="#{url}" alt="#{area_name}" />\n}
331
+ end
332
+ res << "</map>\n"
333
+ # map_file = src.sub(/.dot/, '.map')
334
+ # system("dot -Timap #{src} -o #{map_file}")
335
+ res << %{<img src="#{dot}" usemap="#map" border="0" alt="#{dot}">}
336
+ return res
337
+ end
338
+
339
+ end
340
+
341
+ end
342
+
@@ -0,0 +1,249 @@
1
+ module RDocF95; end
2
+
3
+ module RDocF95::DOT
4
+
5
+ TAB = ' '
6
+ TAB2 = TAB * 2
7
+
8
+ # options for node declaration
9
+ NODE_OPTS = [
10
+ 'bgcolor',
11
+ 'color',
12
+ 'fontcolor',
13
+ 'fontname',
14
+ 'fontsize',
15
+ 'height',
16
+ 'width',
17
+ 'label',
18
+ 'layer',
19
+ 'rank',
20
+ 'shape',
21
+ 'shapefile',
22
+ 'style',
23
+ 'URL',
24
+ ]
25
+
26
+ # options for edge declaration
27
+ EDGE_OPTS = [
28
+ 'color',
29
+ 'decorate',
30
+ 'dir',
31
+ 'fontcolor',
32
+ 'fontname',
33
+ 'fontsize',
34
+ 'id',
35
+ 'label',
36
+ 'layer',
37
+ 'lhead',
38
+ 'ltail',
39
+ 'minlen',
40
+ 'style',
41
+ 'weight'
42
+ ]
43
+
44
+ # options for graph declaration
45
+ GRAPH_OPTS = [
46
+ 'bgcolor',
47
+ 'center',
48
+ 'clusterrank',
49
+ 'color',
50
+ 'compound',
51
+ 'concentrate',
52
+ 'fillcolor',
53
+ 'fontcolor',
54
+ 'fontname',
55
+ 'fontsize',
56
+ 'label',
57
+ 'layerseq',
58
+ 'margin',
59
+ 'mclimit',
60
+ 'nodesep',
61
+ 'nslimit',
62
+ 'ordering',
63
+ 'orientation',
64
+ 'page',
65
+ 'rank',
66
+ 'rankdir',
67
+ 'ranksep',
68
+ 'ratio',
69
+ 'size',
70
+ 'style',
71
+ 'URL'
72
+ ]
73
+
74
+ # a root class for any element in dot notation
75
+ class SimpleElement
76
+ attr_accessor :name
77
+
78
+ def initialize( params = {} )
79
+ @label = params['name'] ? params['name'] : ''
80
+ end
81
+
82
+ def to_s
83
+ @name
84
+ end
85
+ end
86
+
87
+ # an element that has options ( node, edge or graph )
88
+ class Element < SimpleElement
89
+ #attr_reader :parent
90
+ attr_accessor :name, :options
91
+
92
+ def initialize( params = {}, option_list = [] )
93
+ super( params )
94
+ @name = params['name'] ? params['name'] : nil
95
+ @parent = params['parent'] ? params['parent'] : nil
96
+ @options = {}
97
+ option_list.each{ |i|
98
+ @options[i] = params[i] if params[i]
99
+ }
100
+ @options['label'] ||= @name if @name != 'node'
101
+ end
102
+
103
+ def each_option
104
+ @options.each{ |i| yield i }
105
+ end
106
+
107
+ def each_option_pair
108
+ @options.each_pair{ |key, val| yield key, val }
109
+ end
110
+
111
+ #def parent=( thing )
112
+ # @parent.delete( self ) if defined?( @parent ) and @parent
113
+ # @parent = thing
114
+ #end
115
+ end
116
+
117
+
118
+ # this is used when we build nodes that have shape=record
119
+ # ports don't have options :)
120
+ class Port < SimpleElement
121
+ attr_accessor :label
122
+
123
+ def initialize( params = {} )
124
+ super( params )
125
+ @name = params['label'] ? params['label'] : ''
126
+ end
127
+ def to_s
128
+ ( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}"
129
+ end
130
+ end
131
+
132
+ # node element
133
+ class Node < Element
134
+
135
+ def initialize( params = {}, option_list = NODE_OPTS )
136
+ super( params, option_list )
137
+ @ports = params['ports'] ? params['ports'] : []
138
+ end
139
+
140
+ def each_port
141
+ @ports.each{ |i| yield i }
142
+ end
143
+
144
+ def << ( thing )
145
+ @ports << thing
146
+ end
147
+
148
+ def push ( thing )
149
+ @ports.push( thing )
150
+ end
151
+
152
+ def pop
153
+ @ports.pop
154
+ end
155
+
156
+ def to_s( t = '' )
157
+
158
+ label = @options['shape'] != 'record' && @ports.length == 0 ?
159
+ @options['label'] ?
160
+ t + TAB + "label = \"#{@options['label']}\"\n" :
161
+ '' :
162
+ t + TAB + 'label = "' + " \\\n" +
163
+ t + TAB2 + "#{@options['label']}| \\\n" +
164
+ @ports.collect{ |i|
165
+ t + TAB2 + i.to_s
166
+ }.join( "| \\\n" ) + " \\\n" +
167
+ t + TAB + '"' + "\n"
168
+
169
+ t + "#{@name} [\n" +
170
+ @options.to_a.collect{ |i|
171
+ i[1] && i[0] != 'label' ?
172
+ t + TAB + "#{i[0]} = #{i[1]}" : nil
173
+ }.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) +
174
+ label +
175
+ t + "]\n"
176
+ end
177
+ end
178
+
179
+ # subgraph element is the same to graph, but has another header in dot
180
+ # notation
181
+ class Subgraph < Element
182
+
183
+ def initialize( params = {}, option_list = GRAPH_OPTS )
184
+ super( params, option_list )
185
+ @nodes = params['nodes'] ? params['nodes'] : []
186
+ @dot_string = 'subgraph'
187
+ end
188
+
189
+ def each_node
190
+ @nodes.each{ |i| yield i }
191
+ end
192
+
193
+ def << ( thing )
194
+ @nodes << thing
195
+ end
196
+
197
+ def push( thing )
198
+ @nodes.push( thing )
199
+ end
200
+
201
+ def pop
202
+ @nodes.pop
203
+ end
204
+
205
+ def to_s( t = '' )
206
+ hdr = t + "#{@dot_string} #{@name} {\n"
207
+
208
+ options = @options.to_a.collect{ |name, val|
209
+ val && name != 'label' ?
210
+ t + TAB + "#{name} = #{val}" :
211
+ name ? t + TAB + "#{name} = \"#{val}\"" : nil
212
+ }.compact.join( "\n" ) + "\n"
213
+
214
+ nodes = @nodes.collect{ |i|
215
+ i.to_s( t + TAB )
216
+ }.join( "\n" ) + "\n"
217
+ hdr + options + nodes + t + "}\n"
218
+ end
219
+ end
220
+
221
+ # this is graph
222
+ class Digraph < Subgraph
223
+ def initialize( params = {}, option_list = GRAPH_OPTS )
224
+ super( params, option_list )
225
+ @dot_string = 'digraph'
226
+ end
227
+ end
228
+
229
+ # this is edge
230
+ class Edge < Element
231
+ attr_accessor :from, :to
232
+ def initialize( params = {}, option_list = EDGE_OPTS )
233
+ super( params, option_list )
234
+ @from = params['from'] ? params['from'] : nil
235
+ @to = params['to'] ? params['to'] : nil
236
+ end
237
+
238
+ def to_s( t = '' )
239
+ t + "#{@from} -> #{to} [\n" +
240
+ @options.to_a.collect{ |i|
241
+ i[1] && i[0] != 'label' ?
242
+ t + TAB + "#{i[0]} = #{i[1]}" :
243
+ i[1] ? t + TAB + "#{i[0]} = \"#{i[1]}\"" : nil
244
+ }.compact.join( "\n" ) + "\n" + t + "]\n"
245
+ end
246
+ end
247
+
248
+ end
249
+