123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- // graphviz
- package graphviz
- import (
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "os/exec"
- "strings"
- )
- var replacer *strings.Replacer
- func init() {
- replacer = strings.NewReplacer("\n", "\\n", "\r", "\\r", "\t", "\\t")
- }
- type Attributes map[string]string
- func NewAttributes(attributes ...string) Attributes {
- me := make(Attributes)
- for i := 1; i < len(attributes); i += 2 {
- key := attributes[i-1]
- value := replacer.Replace(attributes[i])
- me[key] = value
- }
- return me
- }
- type Node struct {
- Attributes
- ID string
- }
- func NewNode(id string, attributes ...string) *Node {
- me := &Node{}
- me.ID = id
- me.Attributes = NewAttributes(attributes...)
- return me
- }
- func (me Attributes) WriteTo(out io.Writer) {
- comma := false
- if len(me) > 0 {
- fmt.Fprintf(out, "[")
- for k, v := range me {
- if comma {
- fmt.Fprintf(out, ",")
- }
- fmt.Fprintf(out, "%s=\"%s\"", k, v)
- comma = true
- }
- fmt.Fprintf(out, "]")
- }
- }
- func (me Attributes) WriteForGraphTo(out io.Writer) {
- if len(me) > 0 {
- for k, v := range me {
- fmt.Fprintf(out, "%s=\"%s\";\n", k, v)
- }
- }
- }
- func (me *Node) WriteTo(out io.Writer) {
- fmt.Fprintf(out, "%s", me.ID)
- me.Attributes.WriteTo(out)
- fmt.Fprintf(out, ";\n")
- }
- type Edge struct {
- Attributes
- From *Node
- To *Node
- }
- func NewEdge(from, to *Node, attributes ...string) *Edge {
- me := &Edge{}
- me.From = from
- me.To = to
- me.Attributes = NewAttributes(attributes...)
- return me
- }
- func (me *Edge) WriteTo(out io.Writer) {
- if (me.From != nil) && (me.To != nil) {
- fmt.Fprintf(out, "%s -> %s ", me.From.ID, me.To.ID)
- me.Attributes.WriteTo(out)
- fmt.Fprintf(out, ";\n")
- }
- }
- type Digraph struct {
- Attributes
- nodes []*Node
- edges []*Edge
- }
- func NewDigraph(attributes ...string) *Digraph {
- me := &Digraph{}
- me.Attributes = NewAttributes(attributes...)
- return me
- }
- func (me *Digraph) AddNode(id string, attributes ...string) *Node {
- node := NewNode(id, attributes...)
- me.nodes = append(me.nodes, node)
- return node
- }
- func (me *Digraph) AddEdge(from, to *Node, attributes ...string) *Edge {
- edge := NewEdge(from, to, attributes...)
- me.edges = append(me.edges, edge)
- return edge
- }
- func (me *Digraph) FindNode(id string) *Node {
- /* XXX stupid linear search for now... */
- for _, node := range me.nodes {
- if node.ID == id {
- return node
- }
- }
- return nil
- }
- func (me *Digraph) AddEdgeByName(from, to string, attributes ...string) *Edge {
- node_from := me.FindNode(from)
- node_to := me.FindNode(to)
- return me.AddEdge(node_from, node_to, attributes...)
- }
- func (me *Digraph) WriteTo(out io.Writer) {
- fmt.Fprintf(out, "digraph {\n")
- me.Attributes.WriteForGraphTo(out)
- for _, node := range me.nodes {
- node.WriteTo(out)
- }
- for _, edge := range me.edges {
- edge.WriteTo(out)
- }
- fmt.Fprintf(out, "\n}\n")
- }
- func (me *Digraph) Dotty() error {
- file, err := ioutil.TempFile("", "woe_gv_")
- if file == nil {
- return err
- }
- me.WriteTo(file)
- name := file.Name()
- file.Close()
- cmd := exec.Command("dotty", name)
- cmd.Stderr = os.Stderr
- cmd.Stdout = os.Stdout
- err = cmd.Run()
- if err != nil {
- return err
- }
- cmd.Wait()
- return nil
- }
|