github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/dag/graph.go (about)

     1  package dag
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sort"
     7  	"sync"
     8  )
     9  
    10  // Graph is used to represent a dependency graph.
    11  type Graph struct {
    12  	vertices  *Set
    13  	edges     *Set
    14  	downEdges map[interface{}]*Set
    15  	upEdges   map[interface{}]*Set
    16  	once      sync.Once
    17  }
    18  
    19  // Vertex of the graph.
    20  type Vertex interface{}
    21  
    22  // NamedVertex is an optional interface that can be implemented by Vertex
    23  // to give it a human-friendly name that is used for outputting the graph.
    24  type NamedVertex interface {
    25  	Vertex
    26  	Name() string
    27  }
    28  
    29  // Vertices returns the list of all the vertices in the graph.
    30  func (g *Graph) Vertices() []Vertex {
    31  	list := g.vertices.List()
    32  	result := make([]Vertex, len(list))
    33  	for i, v := range list {
    34  		result[i] = v.(Vertex)
    35  	}
    36  
    37  	return result
    38  }
    39  
    40  // Edges returns the list of all the edges in the graph.
    41  func (g *Graph) Edges() []Edge {
    42  	list := g.edges.List()
    43  	result := make([]Edge, len(list))
    44  	for i, v := range list {
    45  		result[i] = v.(Edge)
    46  	}
    47  
    48  	return result
    49  }
    50  
    51  // EdgesFrom returns the list of edges from the given source.
    52  func (g *Graph) EdgesFrom(v Vertex) []Edge {
    53  	var result []Edge
    54  	from := hashcode(v)
    55  	for _, e := range g.Edges() {
    56  		if hashcode(e.Source()) == from {
    57  			result = append(result, e)
    58  		}
    59  	}
    60  
    61  	return result
    62  }
    63  
    64  // EdgesTo returns the list of edges to the given target.
    65  func (g *Graph) EdgesTo(v Vertex) []Edge {
    66  	var result []Edge
    67  	search := hashcode(v)
    68  	for _, e := range g.Edges() {
    69  		if hashcode(e.Target()) == search {
    70  			result = append(result, e)
    71  		}
    72  	}
    73  
    74  	return result
    75  }
    76  
    77  // HasVertex checks if the given Vertex is present in the graph.
    78  func (g *Graph) HasVertex(v Vertex) bool {
    79  	return g.vertices.Include(v)
    80  }
    81  
    82  // HasEdge checks if the given Edge is present in the graph.
    83  func (g *Graph) HasEdge(e Edge) bool {
    84  	return g.edges.Include(e)
    85  }
    86  
    87  // Add adds a vertex to the graph. This is safe to call multiple time with
    88  // the same Vertex.
    89  func (g *Graph) Add(v Vertex) Vertex {
    90  	g.once.Do(g.init)
    91  	g.vertices.Add(v)
    92  	return v
    93  }
    94  
    95  // Remove removes a vertex from the graph. This will also remove any
    96  // edges with this vertex as a source or target.
    97  func (g *Graph) Remove(v Vertex) Vertex {
    98  	// Delete the vertex itself
    99  	g.vertices.Delete(v)
   100  
   101  	// Delete the edges to non-existent things
   102  	for _, target := range g.DownEdges(v).List() {
   103  		g.RemoveEdge(BasicEdge(v, target))
   104  	}
   105  	for _, source := range g.UpEdges(v).List() {
   106  		g.RemoveEdge(BasicEdge(source, v))
   107  	}
   108  
   109  	return nil
   110  }
   111  
   112  // Replace replaces the original Vertex with replacement. If the original
   113  // does not exist within the graph, then false is returned. Otherwise, true
   114  // is returned.
   115  func (g *Graph) Replace(original, replacement Vertex) bool {
   116  	// If we don't have the original, we can't do anything
   117  	if !g.vertices.Include(original) {
   118  		return false
   119  	}
   120  
   121  	// If they're the same, then don't do anything
   122  	if original == replacement {
   123  		return true
   124  	}
   125  
   126  	// Add our new vertex, then copy all the edges
   127  	g.Add(replacement)
   128  	for _, target := range g.DownEdges(original).List() {
   129  		g.Connect(BasicEdge(replacement, target))
   130  	}
   131  	for _, source := range g.UpEdges(original).List() {
   132  		g.Connect(BasicEdge(source, replacement))
   133  	}
   134  
   135  	// Remove our old vertex, which will also remove all the edges
   136  	g.Remove(original)
   137  
   138  	return true
   139  }
   140  
   141  // RemoveEdge removes an edge from the graph.
   142  func (g *Graph) RemoveEdge(edge Edge) {
   143  	g.once.Do(g.init)
   144  
   145  	// Delete the edge from the set
   146  	g.edges.Delete(edge)
   147  
   148  	// Delete the up/down edges
   149  	if s, ok := g.downEdges[hashcode(edge.Source())]; ok {
   150  		s.Delete(edge.Target())
   151  	}
   152  	if s, ok := g.upEdges[hashcode(edge.Target())]; ok {
   153  		s.Delete(edge.Source())
   154  	}
   155  }
   156  
   157  // DownEdges returns the outward edges from the source Vertex v.
   158  func (g *Graph) DownEdges(v Vertex) *Set {
   159  	g.once.Do(g.init)
   160  	return g.downEdges[hashcode(v)]
   161  }
   162  
   163  // UpEdges returns the inward edges to the destination Vertex v.
   164  func (g *Graph) UpEdges(v Vertex) *Set {
   165  	g.once.Do(g.init)
   166  	return g.upEdges[hashcode(v)]
   167  }
   168  
   169  // Connect adds an edge with the given source and target. This is safe to
   170  // call multiple times with the same value. Note that the same value is
   171  // verified through pointer equality of the vertices, not through the
   172  // value of the edge itself.
   173  func (g *Graph) Connect(edge Edge) {
   174  	g.once.Do(g.init)
   175  
   176  	source := edge.Source()
   177  	target := edge.Target()
   178  	sourceCode := hashcode(source)
   179  	targetCode := hashcode(target)
   180  
   181  	// Do we have this already? If so, don't add it again.
   182  	if s, ok := g.downEdges[sourceCode]; ok && s.Include(target) {
   183  		return
   184  	}
   185  
   186  	// Add the edge to the set
   187  	g.edges.Add(edge)
   188  
   189  	// Add the down edge
   190  	s, ok := g.downEdges[sourceCode]
   191  	if !ok {
   192  		s = new(Set)
   193  		g.downEdges[sourceCode] = s
   194  	}
   195  	s.Add(target)
   196  
   197  	// Add the up edge
   198  	s, ok = g.upEdges[targetCode]
   199  	if !ok {
   200  		s = new(Set)
   201  		g.upEdges[targetCode] = s
   202  	}
   203  	s.Add(source)
   204  }
   205  
   206  // String outputs some human-friendly output for the graph structure.
   207  func (g *Graph) StringWithNodeTypes() string {
   208  	var buf bytes.Buffer
   209  
   210  	// Build the list of node names and a mapping so that we can more
   211  	// easily alphabetize the output to remain deterministic.
   212  	vertices := g.Vertices()
   213  	names := make([]string, 0, len(vertices))
   214  	mapping := make(map[string]Vertex, len(vertices))
   215  	for _, v := range vertices {
   216  		name := VertexName(v)
   217  		names = append(names, name)
   218  		mapping[name] = v
   219  	}
   220  	sort.Strings(names)
   221  
   222  	// Write each node in order...
   223  	for _, name := range names {
   224  		v := mapping[name]
   225  		targets := g.downEdges[hashcode(v)]
   226  
   227  		buf.WriteString(fmt.Sprintf("%s - %T\n", name, v))
   228  
   229  		// Alphabetize dependencies
   230  		deps := make([]string, 0, targets.Len())
   231  		targetNodes := make(map[string]Vertex)
   232  		for _, target := range targets.List() {
   233  			dep := VertexName(target)
   234  			deps = append(deps, dep)
   235  			targetNodes[dep] = target
   236  		}
   237  		sort.Strings(deps)
   238  
   239  		// Write dependencies
   240  		for _, d := range deps {
   241  			buf.WriteString(fmt.Sprintf("  %s - %T\n", d, targetNodes[d]))
   242  		}
   243  	}
   244  
   245  	return buf.String()
   246  }
   247  
   248  // String outputs some human-friendly output for the graph structure.
   249  func (g *Graph) String() string {
   250  	var buf bytes.Buffer
   251  
   252  	// Build the list of node names and a mapping so that we can more
   253  	// easily alphabetize the output to remain deterministic.
   254  	vertices := g.Vertices()
   255  	names := make([]string, 0, len(vertices))
   256  	mapping := make(map[string]Vertex, len(vertices))
   257  	for _, v := range vertices {
   258  		name := VertexName(v)
   259  		names = append(names, name)
   260  		mapping[name] = v
   261  	}
   262  	sort.Strings(names)
   263  
   264  	// Write each node in order...
   265  	for _, name := range names {
   266  		v := mapping[name]
   267  		targets := g.downEdges[hashcode(v)]
   268  
   269  		buf.WriteString(fmt.Sprintf("%s\n", name))
   270  
   271  		// Alphabetize dependencies
   272  		deps := make([]string, 0, targets.Len())
   273  		for _, target := range targets.List() {
   274  			deps = append(deps, VertexName(target))
   275  		}
   276  		sort.Strings(deps)
   277  
   278  		// Write dependencies
   279  		for _, d := range deps {
   280  			buf.WriteString(fmt.Sprintf("  %s\n", d))
   281  		}
   282  	}
   283  
   284  	return buf.String()
   285  }
   286  
   287  func (g *Graph) init() {
   288  	g.vertices = new(Set)
   289  	g.edges = new(Set)
   290  	g.downEdges = make(map[interface{}]*Set)
   291  	g.upEdges = make(map[interface{}]*Set)
   292  }
   293  
   294  // VertexName returns the name of a vertex.
   295  func VertexName(raw Vertex) string {
   296  	switch v := raw.(type) {
   297  	case NamedVertex:
   298  		return v.Name()
   299  	case fmt.Stringer:
   300  		return fmt.Sprintf("%s", v)
   301  	default:
   302  		return fmt.Sprintf("%v", v)
   303  	}
   304  }