diff options
Diffstat (limited to 'lib/puppet/external/gratr/rdot.rb')
-rw-r--r-- | lib/puppet/external/gratr/rdot.rb | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/lib/puppet/external/gratr/rdot.rb b/lib/puppet/external/gratr/rdot.rb new file mode 100644 index 000000000..4ce545ae6 --- /dev/null +++ b/lib/puppet/external/gratr/rdot.rb @@ -0,0 +1,327 @@ +# rdot.rb +# +# $Id$ +# +# This is a modified version of dot.rb from Dave Thomas's rdoc project. I [Horst Duchene] +# renamed it to rdot.rb to avoid collision with an installed rdoc/dot. +# +# It also supports undirected edges. + +module DOT + + # These glogal vars are used to make nice graph source. + + $tab = ' ' + $tab2 = $tab * 2 + + # if we don't like 4 spaces, we can change it any time + + def change_tab (t) + $tab = t + $tab2 = t * 2 + end + + # options for node declaration + + NODE_OPTS = [ + # attributes due to + # http://www.graphviz.org/Documentation/dotguide.pdf + # March, 26, 2005 + 'bottomlabel', # auxiliary label for nodes of shape M* + 'color', # default: black; node shape color + 'comment', # any string (format-dependent) + 'distortion', # default: 0.0; node distortion for shape=polygon + 'fillcolor', # default: lightgrey/black; node fill color + 'fixedsize', # default: false; label text has no affect on node size + 'fontcolor', # default: black; type face color + 'fontname', # default: Times-Roman; font family + 'fontsize', #default: 14; point size of label + 'group', # name of nodes group + 'height', # default: .5; height in inches + 'label', # default: node name; any string + 'layer', # default: overlay range; all, id or id:id + 'orientation', # dafault: 0.0; node rotation angle + 'peripheries', # shape-dependent number of node boundaries + 'regular', # default: false; force polygon to be regular + 'shape', # default: ellipse; node shape; see Section 2.1 and Appendix E + 'shapefile', # external EPSF or SVG custom shape file + 'sides', # default: 4; number of sides for shape=polygon + 'skew' , # default: 0.0; skewing of node for shape=polygon + 'style', # graphics options, e.g. bold, dotted, filled; cf. Section 2.3 + 'toplabel', # auxiliary label for nodes of shape M* + 'URL', # URL associated with node (format-dependent) + 'width', # default: .75; width in inches + 'z', #default: 0.0; z coordinate for VRML output + + # maintained for backward compatibility or rdot internal + 'bgcolor', + 'rank' + ] + + # options for edge declaration + + EDGE_OPTS = [ + 'arrowhead', # default: normal; style of arrowhead at head end + 'arrowsize', # default: 1.0; scaling factor for arrowheads + 'arrowtail', # default: normal; style of arrowhead at tail end + 'color', # default: black; edge stroke color + 'comment', # any string (format-dependent) + 'constraint', # default: true use edge to affect node ranking + 'decorate', # if set, draws a line connecting labels with their edges + 'dir', # default: forward; forward, back, both, or none + 'fontcolor', # default: black type face color + 'fontname', # default: Times-Roman; font family + 'fontsize', # default: 14; point size of label + 'headlabel', # label placed near head of edge + 'headport', # n,ne,e,se,s,sw,w,nw + 'headURL', # URL attached to head label if output format is ismap + 'label', # edge label + 'labelangle', # default: -25.0; angle in degrees which head or tail label is rotated off edge + 'labeldistance', # default: 1.0; scaling factor for distance of head or tail label from node + 'labelfloat', # default: false; lessen constraints on edge label placement + 'labelfontcolor', # default: black; type face color for head and tail labels + 'labelfontname', # default: Times-Roman; font family for head and tail labels + 'labelfontsize', # default: 14 point size for head and tail labels + 'layer', # default: overlay range; all, id or id:id + 'lhead', # name of cluster to use as head of edge + 'ltail', # name of cluster to use as tail of edge + 'minlen', # default: 1 minimum rank distance between head and tail + 'samehead', # tag for head node; edge heads with the same tag are merged onto the same port + 'sametail', # tag for tail node; edge tails with the same tag are merged onto the same port + 'style', # graphics options, e.g. bold, dotted, filled; cf. Section 2.3 + 'taillabel', # label placed near tail of edge + 'tailport', # n,ne,e,se,s,sw,w,nw + 'tailURL', # URL attached to tail label if output format is ismap + 'weight', # default: 1; integer cost of stretching an edge + + # maintained for backward compatibility or rdot internal + 'id' + ] + + # options for graph declaration + + GRAPH_OPTS = [ + 'bgcolor', + 'center', 'clusterrank', 'color', 'concentrate', + 'fontcolor', 'fontname', 'fontsize', + 'label', 'layerseq', + 'margin', 'mclimit', + 'nodesep', 'nslimit', + 'ordering', 'orientation', + 'page', + 'rank', 'rankdir', 'ranksep', 'ratio', + 'size' + ] + + # a root class for any element in dot notation + + class DOTSimpleElement + + attr_accessor :name + + def initialize (params = {}) + @label = params['name'] ? params['name'] : '' + end + + def to_s + @name + end + end + + # an element that has options ( node, edge, or graph ) + + class DOTElement < DOTSimpleElement + + # attr_reader :parent + attr_accessor :name, :options + + def initialize (params = {}, option_list = []) + super(params) + @name = params['name'] ? params['name'] : nil + @parent = params['parent'] ? params['parent'] : nil + @options = {} + option_list.each{ |i| + @options[i] = params[i] if params[i] + } + @options['label'] ||= @name if @name != 'node' + end + + def each_option + @options.each{ |i| yield i } + end + + def each_option_pair + @options.each_pair{ |key, val| yield key, val } + end + + #def parent=( thing ) + # @parent.delete( self ) if defined?( @parent ) and @parent + # @parent = thing + #end + + end + + + # This is used when we build nodes that have shape=record + # ports don't have options :) + + class DOTPort < DOTSimpleElement + + attr_accessor :label + + def initialize (params = {}) + super(params) + @name = params['label'] ? params['label'] : '' + end + + def to_s + ( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}" + end + end + + # node element + + class DOTNode < DOTElement + + @ports + + def initialize (params = {}, option_list = NODE_OPTS) + super(params, option_list) + @ports = params['ports'] ? params['ports'] : [] + end + + def each_port + @ports.each { |i| yield i } + end + + def << (thing) + @ports << thing + end + + def push (thing) + @ports.push(thing) + end + + def pop + @ports.pop + end + + def to_s (t = '') + + # This code is totally incomprehensible; it needs to be replaced! + + label = @options['shape'] != 'record' && @ports.length == 0 ? + @options['label'] ? + t + $tab + "label = \"#{@options['label']}\"\n" : + '' : + t + $tab + 'label = "' + " \\\n" + + t + $tab2 + "#{@options['label']}| \\\n" + + @ports.collect{ |i| + t + $tab2 + i.to_s + }.join( "| \\\n" ) + " \\\n" + + t + $tab + '"' + "\n" + + t + "#{@name} [\n" + + @options.to_a.collect{ |i| + i[1] && i[0] != 'label' ? + t + $tab + "#{i[0]} = #{i[1]}" : nil + }.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) + + label + + t + "]\n" + end + + end # class DOTNode + + # A subgraph element is the same to graph, but has another header in dot + # notation. + + class DOTSubgraph < DOTElement + + @nodes + @dot_string + + def initialize (params = {}, option_list = GRAPH_OPTS) + super(params, option_list) + @nodes = params['nodes'] ? params['nodes'] : [] + @dot_string = 'graph' + end + + def each_node + @nodes.each{ |i| yield i } + end + + def << (thing) + @nodes << thing + end + + def push (thing) + @nodes.push( thing ) + end + + def pop + @nodes.pop + end + + def to_s (t = '') + hdr = t + "#{@dot_string} #{@name} {\n" + + options = @options.to_a.collect{ |name, val| + val && name != 'label' ? + t + $tab + "#{name} = #{val}" : + name ? t + $tab + "#{name} = \"#{val}\"" : nil + }.compact.join( "\n" ) + "\n" + + nodes = @nodes.collect{ |i| + i.to_s( t + $tab ) + }.join( "\n" ) + "\n" + hdr + options + nodes + t + "}\n" + end + + end # class DOTSubgraph + + # This is a graph. + + class DOTDigraph < DOTSubgraph + + def initialize (params = {}, option_list = GRAPH_OPTS) + super(params, option_list) + @dot_string = 'digraph' + end + + end # class DOTDigraph + + # This is an edge. + + class DOTEdge < DOTElement + + attr_accessor :from, :to + + def initialize (params = {}, option_list = EDGE_OPTS) + super(params, option_list) + @from = params['from'] ? params['from'] : nil + @to = params['to'] ? params['to'] : nil + end + + def edge_link + '--' + end + + def to_s (t = '') + t + "#{@from} #{edge_link} #{to} [\n" + + @options.to_a.collect{ |i| + i[1] && i[0] != 'label' ? + t + $tab + "#{i[0]} = #{i[1]}" : + i[1] ? t + $tab + "#{i[0]} = \"#{i[1]}\"" : nil + }.compact.join( "\n" ) + "\n" + t + "]\n" + end + + end # class DOTEdge + + class DOTDirectedEdge < DOTEdge + + def edge_link + '->' + end + + end # class DOTDirectedEdge +end # module DOT |