graphviz.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // graphviz
  2. package graphviz
  3. import (
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "os/exec"
  9. "strings"
  10. )
  11. var replacer *strings.Replacer
  12. func init() {
  13. replacer = strings.NewReplacer("\n", "\\n", "\r", "\\r", "\t", "\\t")
  14. }
  15. type Attributes map[string]string
  16. func NewAttributes(attributes ...string) Attributes {
  17. me := make(Attributes)
  18. for i := 1; i < len(attributes); i += 2 {
  19. key := attributes[i-1]
  20. value := replacer.Replace(attributes[i])
  21. me[key] = value
  22. }
  23. return me
  24. }
  25. type Node struct {
  26. Attributes
  27. ID string
  28. }
  29. func NewNode(id string, attributes ...string) *Node {
  30. me := &Node{}
  31. me.ID = id
  32. me.Attributes = NewAttributes(attributes...)
  33. return me
  34. }
  35. func (me Attributes) WriteTo(out io.Writer) {
  36. comma := false
  37. if len(me) > 0 {
  38. fmt.Fprintf(out, "[")
  39. for k, v := range me {
  40. if comma {
  41. fmt.Fprintf(out, ",")
  42. }
  43. fmt.Fprintf(out, "%s=\"%s\"", k, v)
  44. comma = true
  45. }
  46. fmt.Fprintf(out, "]")
  47. }
  48. }
  49. func (me Attributes) WriteForGraphTo(out io.Writer) {
  50. if len(me) > 0 {
  51. for k, v := range me {
  52. fmt.Fprintf(out, "%s=\"%s\";\n", k, v)
  53. }
  54. }
  55. }
  56. func (me *Node) WriteTo(out io.Writer) {
  57. fmt.Fprintf(out, "%s", me.ID)
  58. me.Attributes.WriteTo(out)
  59. fmt.Fprintf(out, ";\n")
  60. }
  61. type Edge struct {
  62. Attributes
  63. From *Node
  64. To *Node
  65. }
  66. func NewEdge(from, to *Node, attributes ...string) *Edge {
  67. me := &Edge{}
  68. me.From = from
  69. me.To = to
  70. me.Attributes = NewAttributes(attributes...)
  71. return me
  72. }
  73. func (me *Edge) WriteTo(out io.Writer) {
  74. if (me.From != nil) && (me.To != nil) {
  75. fmt.Fprintf(out, "%s -> %s ", me.From.ID, me.To.ID)
  76. me.Attributes.WriteTo(out)
  77. fmt.Fprintf(out, ";\n")
  78. }
  79. }
  80. type Digraph struct {
  81. Attributes
  82. nodes []*Node
  83. edges []*Edge
  84. }
  85. func NewDigraph(attributes ...string) *Digraph {
  86. me := &Digraph{}
  87. me.Attributes = NewAttributes(attributes...)
  88. return me
  89. }
  90. func (me *Digraph) AddNode(id string, attributes ...string) *Node {
  91. node := NewNode(id, attributes...)
  92. me.nodes = append(me.nodes, node)
  93. return node
  94. }
  95. func (me *Digraph) AddEdge(from, to *Node, attributes ...string) *Edge {
  96. edge := NewEdge(from, to, attributes...)
  97. me.edges = append(me.edges, edge)
  98. return edge
  99. }
  100. func (me *Digraph) FindNode(id string) *Node {
  101. /* XXX stupid linear search for now... */
  102. for _, node := range me.nodes {
  103. if node.ID == id {
  104. return node
  105. }
  106. }
  107. return nil
  108. }
  109. func (me *Digraph) AddEdgeByName(from, to string, attributes ...string) *Edge {
  110. node_from := me.FindNode(from)
  111. node_to := me.FindNode(to)
  112. return me.AddEdge(node_from, node_to, attributes...)
  113. }
  114. func (me *Digraph) WriteTo(out io.Writer) {
  115. fmt.Fprintf(out, "digraph {\n")
  116. me.Attributes.WriteForGraphTo(out)
  117. for _, node := range me.nodes {
  118. node.WriteTo(out)
  119. }
  120. for _, edge := range me.edges {
  121. edge.WriteTo(out)
  122. }
  123. fmt.Fprintf(out, "\n}\n")
  124. }
  125. func (me *Digraph) Dotty() error {
  126. file, err := ioutil.TempFile("", "woe_gv_")
  127. if file == nil {
  128. return err
  129. }
  130. me.WriteTo(file)
  131. name := file.Name()
  132. file.Close()
  133. cmd := exec.Command("dotty", name)
  134. cmd.Stderr = os.Stderr
  135. cmd.Stdout = os.Stdout
  136. err = cmd.Run()
  137. if err != nil {
  138. return err
  139. }
  140. cmd.Wait()
  141. return nil
  142. }