Skip to content

Commit 4ecf62e

Browse files
authored
added deepcopy (#42)
1 parent c43e88f commit 4ecf62e

File tree

2 files changed

+167
-0
lines changed

2 files changed

+167
-0
lines changed

graph.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,3 +427,72 @@ func (am *AttributesMap) GetAttributes() map[string]interface{} {
427427
}
428428
return copyMap
429429
}
430+
431+
// DeepCopy creates a deep copy of a Graph, including all nodes, edges, subgraphs & attributes
432+
func (g *Graph) DeepCopy() *Graph {
433+
copy := NewGraph()
434+
copy.id = g.id
435+
copy.isStrict = g.isStrict
436+
copy.graphType = g.graphType
437+
copy.seq = g.seq
438+
copy.parent = g.parent
439+
440+
copy.AttributesMap = AttributesMap{attributes: g.GetAttributes()}
441+
442+
copy.nodes = make(map[string]Node, len(g.nodes))
443+
for id, node := range g.nodes {
444+
copy.nodes[id] = Node{
445+
AttributesMap: AttributesMap{attributes: node.GetAttributes()},
446+
graph: copy,
447+
id: node.id,
448+
seq: node.seq,
449+
}
450+
}
451+
452+
copy.edgesFrom = make(map[string][]Edge, len(g.edgesFrom))
453+
for from, edges := range g.edgesFrom {
454+
newEdges := make([]Edge, len(edges))
455+
for i, edge := range edges {
456+
newEdges[i] = Edge{
457+
AttributesMap: AttributesMap{attributes: edge.GetAttributes()},
458+
graph: copy,
459+
from: copy.nodes[edge.from.id],
460+
to: copy.nodes[edge.to.id],
461+
fromPort: edge.fromPort,
462+
toPort: edge.toPort,
463+
}
464+
}
465+
copy.edgesFrom[from] = newEdges
466+
}
467+
468+
copy.subgraphs = make(map[string]*Graph, len(g.subgraphs))
469+
keys := make([]string, 0, len(g.subgraphs))
470+
for id := range g.subgraphs {
471+
keys = append(keys, id)
472+
}
473+
sort.Strings(keys)
474+
for _, id := range keys {
475+
newSubgraph := g.subgraphs[id].DeepCopy()
476+
newSubgraph.parent = copy
477+
copy.subgraphs[id] = newSubgraph
478+
}
479+
480+
copy.sameRank = make(map[string][]Node, len(g.sameRank))
481+
rankKeys := make([]string, 0, len(g.sameRank))
482+
for rank := range g.sameRank {
483+
rankKeys = append(rankKeys, rank)
484+
}
485+
sort.Strings(rankKeys)
486+
for _, rank := range rankKeys {
487+
newNodes := make([]Node, len(g.sameRank[rank]))
488+
for i, node := range g.sameRank[rank] {
489+
newNodes[i] = copy.nodes[node.id]
490+
}
491+
copy.sameRank[rank] = newNodes
492+
}
493+
494+
copy.nodeInitializer = g.nodeInitializer
495+
copy.edgeInitializer = g.edgeInitializer
496+
497+
return copy
498+
}

graph_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dot
33
import (
44
"os"
55
"reflect"
6+
"sort"
67
"strings"
78
"testing"
89
)
@@ -426,3 +427,100 @@ func TestNodeGetAttributesCopy(t *testing.T) {
426427
t.Errorf("expected foo=bar, got %v", attrs2)
427428
}
428429
}
430+
431+
func TestDeepCopy(t *testing.T) {
432+
g := NewGraph(Directed)
433+
g.ID("original")
434+
g.Attr("color", "blue")
435+
436+
n1 := g.Node("A").Attr("label", "Node A")
437+
n2 := g.Node("B").Attr("label", "Node B")
438+
n3 := g.Node("C").Attr("label", "Node C")
439+
440+
e1 := g.Edge(n1, n2).Label("A to B")
441+
e1.Attr("weight", 2)
442+
443+
sub := g.Subgraph("sub1")
444+
sub.Node("D").Attr("label", "Node D")
445+
446+
g.AddToSameRank("top-row", n1, n2, n3)
447+
448+
copy := g.DeepCopy()
449+
450+
if copy.GetID() != g.GetID() {
451+
t.Errorf("Expected graph ID %v, got %v", g.GetID(), copy.GetID())
452+
}
453+
454+
if copy.Value("color") != g.Value("color") {
455+
t.Errorf("Expected graph color %v, got %v", g.Value("color"), copy.Value("color"))
456+
}
457+
458+
if len(copy.nodes) != len(g.nodes) {
459+
t.Errorf("Expected %d nodes, got %d", len(g.nodes), len(copy.nodes))
460+
}
461+
for id, originalNode := range g.nodes {
462+
copiedNode, exists := copy.nodes[id]
463+
if !exists {
464+
t.Errorf("Node %v missing in copied graph", id)
465+
}
466+
if copiedNode.Value("label") != originalNode.Value("label") {
467+
t.Errorf("Node %v: expected label %v, got %v", id, originalNode.Value("label"), copiedNode.Value("label"))
468+
}
469+
}
470+
471+
if len(copy.edgesFrom) != len(g.edgesFrom) {
472+
t.Errorf("Expected %d edges, got %d", len(g.edgesFrom), len(copy.edgesFrom))
473+
}
474+
for from, edges := range g.edgesFrom {
475+
copiedEdges, exists := copy.edgesFrom[from]
476+
if !exists {
477+
t.Errorf("Edges from %v missing in copied graph", from)
478+
}
479+
for i, edge := range edges {
480+
if copiedEdges[i].Value("label") != edge.Value("label") {
481+
t.Errorf("Edge from %v: expected label %v, got %v", from, edge.Value("label"), copiedEdges[i].Value("label"))
482+
}
483+
if copiedEdges[i].Value("weight") != edge.Value("weight") {
484+
t.Errorf("Edge from %v: expected weight %v, got %v", from, edge.Value("weight"), copiedEdges[i].Value("weight"))
485+
}
486+
}
487+
}
488+
489+
keys := make([]string, 0, len(g.subgraphs))
490+
for id := range g.subgraphs {
491+
keys = append(keys, id)
492+
}
493+
sort.Strings(keys)
494+
if len(copy.subgraphs) != len(g.subgraphs) {
495+
t.Errorf("Expected %d subgraphs, got %d", len(g.subgraphs), len(copy.subgraphs))
496+
}
497+
for _, id := range keys {
498+
originalSubgraph := g.subgraphs[id]
499+
copiedSubgraph, exists := copy.subgraphs[id]
500+
if !exists {
501+
t.Errorf("Subgraph %v missing in copied graph", id)
502+
}
503+
if copiedSubgraph.GetID() != originalSubgraph.GetID() {
504+
t.Errorf("Subgraph %v: expected ID %v, got %v", id, originalSubgraph.GetID(), copiedSubgraph.GetID())
505+
}
506+
}
507+
508+
rankKeys := make([]string, 0, len(g.sameRank))
509+
for rank := range g.sameRank {
510+
rankKeys = append(rankKeys, rank)
511+
}
512+
sort.Strings(rankKeys)
513+
if len(copy.sameRank) != len(g.sameRank) {
514+
t.Errorf("Expected sameRank groups to be copied")
515+
}
516+
for _, rank := range rankKeys {
517+
originalNodes := g.sameRank[rank]
518+
copiedNodes, exists := copy.sameRank[rank]
519+
if !exists {
520+
t.Errorf("Same-rank group %v missing in copied graph", rank)
521+
}
522+
if len(copiedNodes) != len(originalNodes) {
523+
t.Errorf("Same-rank group %v: expected %d nodes, got %d", rank, len(originalNodes), len(copiedNodes))
524+
}
525+
}
526+
}

0 commit comments

Comments
 (0)