github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/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 // HasVertex checks if the given Vertex is present in the graph. 52 func (g *Graph) HasVertex(v Vertex) bool { 53 return g.vertices.Include(v) 54 } 55 56 // HasEdge checks if the given Edge is present in the graph. 57 func (g *Graph) HasEdge(e Edge) bool { 58 return g.edges.Include(e) 59 } 60 61 // Add adds a vertex to the graph. This is safe to call multiple time with 62 // the same Vertex. 63 func (g *Graph) Add(v Vertex) Vertex { 64 g.once.Do(g.init) 65 g.vertices.Add(v) 66 return v 67 } 68 69 // Remove removes a vertex from the graph. This will also remove any 70 // edges with this vertex as a source or target. 71 func (g *Graph) Remove(v Vertex) Vertex { 72 // Delete the vertex itself 73 g.vertices.Delete(v) 74 75 // Delete the edges to non-existent things 76 for _, target := range g.DownEdges(v).List() { 77 g.RemoveEdge(BasicEdge(v, target)) 78 } 79 for _, source := range g.UpEdges(v).List() { 80 g.RemoveEdge(BasicEdge(source, v)) 81 } 82 83 return nil 84 } 85 86 // Replace replaces the original Vertex with replacement. If the original 87 // does not exist within the graph, then false is returned. Otherwise, true 88 // is returned. 89 func (g *Graph) Replace(original, replacement Vertex) bool { 90 // If we don't have the original, we can't do anything 91 if !g.vertices.Include(original) { 92 return false 93 } 94 95 // If they're the same, then don't do anything 96 if original == replacement { 97 return true 98 } 99 100 // Add our new vertex, then copy all the edges 101 g.Add(replacement) 102 for _, target := range g.DownEdges(original).List() { 103 g.Connect(BasicEdge(replacement, target)) 104 } 105 for _, source := range g.UpEdges(original).List() { 106 g.Connect(BasicEdge(source, replacement)) 107 } 108 109 // Remove our old vertex, which will also remove all the edges 110 g.Remove(original) 111 112 return true 113 } 114 115 // RemoveEdge removes an edge from the graph. 116 func (g *Graph) RemoveEdge(edge Edge) { 117 g.once.Do(g.init) 118 119 // Delete the edge from the set 120 g.edges.Delete(edge) 121 122 // Delete the up/down edges 123 if s, ok := g.downEdges[hashcode(edge.Source())]; ok { 124 s.Delete(edge.Target()) 125 } 126 if s, ok := g.upEdges[hashcode(edge.Target())]; ok { 127 s.Delete(edge.Source()) 128 } 129 } 130 131 // DownEdges returns the outward edges from the source Vertex v. 132 func (g *Graph) DownEdges(v Vertex) *Set { 133 g.once.Do(g.init) 134 return g.downEdges[hashcode(v)] 135 } 136 137 // UpEdges returns the inward edges to the destination Vertex v. 138 func (g *Graph) UpEdges(v Vertex) *Set { 139 g.once.Do(g.init) 140 return g.upEdges[hashcode(v)] 141 } 142 143 // Connect adds an edge with the given source and target. This is safe to 144 // call multiple times with the same value. Note that the same value is 145 // verified through pointer equality of the vertices, not through the 146 // value of the edge itself. 147 func (g *Graph) Connect(edge Edge) { 148 g.once.Do(g.init) 149 150 source := edge.Source() 151 target := edge.Target() 152 sourceCode := hashcode(source) 153 targetCode := hashcode(target) 154 155 // Do we have this already? If so, don't add it again. 156 if s, ok := g.downEdges[sourceCode]; ok && s.Include(target) { 157 return 158 } 159 160 // Add the edge to the set 161 g.edges.Add(edge) 162 163 // Add the down edge 164 s, ok := g.downEdges[sourceCode] 165 if !ok { 166 s = new(Set) 167 g.downEdges[sourceCode] = s 168 } 169 s.Add(target) 170 171 // Add the up edge 172 s, ok = g.upEdges[targetCode] 173 if !ok { 174 s = new(Set) 175 g.upEdges[targetCode] = s 176 } 177 s.Add(source) 178 } 179 180 // String outputs some human-friendly output for the graph structure. 181 func (g *Graph) StringWithNodeTypes() string { 182 var buf bytes.Buffer 183 184 // Build the list of node names and a mapping so that we can more 185 // easily alphabetize the output to remain deterministic. 186 vertices := g.Vertices() 187 names := make([]string, 0, len(vertices)) 188 mapping := make(map[string]Vertex, len(vertices)) 189 for _, v := range vertices { 190 name := VertexName(v) 191 names = append(names, name) 192 mapping[name] = v 193 } 194 sort.Strings(names) 195 196 // Write each node in order... 197 for _, name := range names { 198 v := mapping[name] 199 targets := g.downEdges[hashcode(v)] 200 201 buf.WriteString(fmt.Sprintf("%s - %T\n", name, v)) 202 203 // Alphabetize dependencies 204 deps := make([]string, 0, targets.Len()) 205 targetNodes := make([]Vertex, 0, targets.Len()) 206 for _, target := range targets.List() { 207 deps = append(deps, VertexName(target)) 208 targetNodes = append(targetNodes, target) 209 } 210 sort.Strings(deps) 211 212 // Write dependencies 213 for i, d := range deps { 214 buf.WriteString(fmt.Sprintf(" %s - %T\n", d, targetNodes[i])) 215 } 216 } 217 218 return buf.String() 219 } 220 221 // String outputs some human-friendly output for the graph structure. 222 func (g *Graph) String() string { 223 var buf bytes.Buffer 224 225 // Build the list of node names and a mapping so that we can more 226 // easily alphabetize the output to remain deterministic. 227 vertices := g.Vertices() 228 names := make([]string, 0, len(vertices)) 229 mapping := make(map[string]Vertex, len(vertices)) 230 for _, v := range vertices { 231 name := VertexName(v) 232 names = append(names, name) 233 mapping[name] = v 234 } 235 sort.Strings(names) 236 237 // Write each node in order... 238 for _, name := range names { 239 v := mapping[name] 240 targets := g.downEdges[hashcode(v)] 241 242 buf.WriteString(fmt.Sprintf("%s\n", name)) 243 244 // Alphabetize dependencies 245 deps := make([]string, 0, targets.Len()) 246 for _, target := range targets.List() { 247 deps = append(deps, VertexName(target)) 248 } 249 sort.Strings(deps) 250 251 // Write dependencies 252 for _, d := range deps { 253 buf.WriteString(fmt.Sprintf(" %s\n", d)) 254 } 255 } 256 257 return buf.String() 258 } 259 260 func (g *Graph) init() { 261 g.vertices = new(Set) 262 g.edges = new(Set) 263 g.downEdges = make(map[interface{}]*Set) 264 g.upEdges = make(map[interface{}]*Set) 265 } 266 267 // VertexName returns the name of a vertex. 268 func VertexName(raw Vertex) string { 269 switch v := raw.(type) { 270 case NamedVertex: 271 return v.Name() 272 case fmt.Stringer: 273 return fmt.Sprintf("%s", v) 274 default: 275 return fmt.Sprintf("%v", v) 276 } 277 }