Current File : //opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/external/dot.rb |
# rdot.rb
#
#
# 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 global 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 node's group
'height', # default: .5; height in inches
'label', # default: node name; any string
'layer', # default: overlay range; all, id or id:id
'orientation', # default: 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
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 = #{stringify(@options['label'])}\n" :
'' :
t + $tab + 'label = "' + " \\\n" +
t + $tab2 + "#{stringify(@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
private
def stringify(s)
%("#{s.gsub('"', '\\"')}")
end
end
# A subgraph element is the same to graph, but has another header in dot
# notation.
class DOTSubgraph < DOTElement
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
# This is a graph.
class DOTDigraph < DOTSubgraph
def initialize(params = {}, option_list = GRAPH_OPTS)
super(params, option_list)
@dot_string = 'digraph'
end
end
# 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 DOTDirectedEdge < DOTEdge
def edge_link
'->'
end
end
end