diff --git a/pkg/application/arch/tequila/incl_viz.go b/pkg/application/arch/tequila/incl_viz.go index 8657ba4348c21fff6c355819e608ae5727427698..c7b87a5043c6fcdd79002afd6390307c8ad7a5e6 100644 --- a/pkg/application/arch/tequila/incl_viz.go +++ b/pkg/application/arch/tequila/incl_viz.go @@ -2,7 +2,7 @@ package tequila import ( "github.com/awalterschulze/gographviz" - "github.com/dghubble/trie" + "github.com/phodal/coca/pkg/application/arch/tequila/trie" "sort" "strconv" "strings" @@ -153,7 +153,7 @@ func (fullGraph *FullGraph) ToDot(split string, include func(string) bool) *gogr return graph } -func (fullGraph *FullGraph) ToMapDot(node *GraphNode) *gographviz.Graph { +func (fullGraph *FullGraph) ToMapDot(trie *trie.PathTrie) *gographviz.Graph { graph := gographviz.NewGraph() _ = graph.SetName("G") @@ -161,7 +161,9 @@ func (fullGraph *FullGraph) ToMapDot(node *GraphNode) *gographviz.Graph { fullGraph.layerIndex = 1 fullGraph.nodeIndex = 1 - fullGraph.buildGraphNode("G", node, graph, nodes) + for _, child := range trie.Children { + fullGraph.buildGraphNode("G", child, graph, nodes) + } for key := range fullGraph.RelationList { relation := fullGraph.RelationList[key] @@ -179,18 +181,18 @@ func (fullGraph *FullGraph) ToMapDot(node *GraphNode) *gographviz.Graph { return graph } -func (fullGraph *FullGraph) buildGraphNode(subgraph string, current *GraphNode, graph *gographviz.Graph, nodes map[string]string) { - layerAttr, layerName := buildLayerAttr(current.text, fullGraph.layerIndex) +func (fullGraph *FullGraph) buildGraphNode(subgraph string, current *trie.PathTrie, graph *gographviz.Graph, nodes map[string]string) { + layerAttr, layerName := buildLayerAttr(current.Value, fullGraph.layerIndex) _ = graph.AddSubGraph(subgraph, layerName, layerAttr) fullGraph.layerIndex++ - if len(current.children) > 0 { - for _, child := range current.children { + if len(current.Children) > 0 { + for _, child := range current.Children { fullGraph.buildGraphNode(layerName, child, graph, nodes) } } else { - _ = graph.AddNode(subgraph, "node"+strconv.Itoa(fullGraph.nodeIndex), fullGraph.buildRelationAttr(current.text)) - nodes[current.text] = "node" + strconv.Itoa(fullGraph.nodeIndex) + _ = graph.AddNode(subgraph, "node"+strconv.Itoa(fullGraph.nodeIndex), fullGraph.buildRelationAttr(current.Value)) + nodes[current.Value] = "node" + strconv.Itoa(fullGraph.nodeIndex) fullGraph.nodeIndex++ } } @@ -200,21 +202,13 @@ type GraphNode struct { children []*GraphNode } -func (fullGraph *FullGraph) BuildMapTree(split string, include func(key string) bool) *GraphNode { - graphNode := &GraphNode{} - +func (fullGraph *FullGraph) BuildMapTree(include func(key string) bool) *trie.PathTrie { pkgTrie := trie.NewPathTrie() for nodeKey := range fullGraph.NodeList { - pkgTrie.Put(strings.ReplaceAll(nodeKey, ".", "/"), 0) - } - - for nodeKey := range fullGraph.NodeList { - tmp := strings.Split(nodeKey, split) - graphNode.text = tmp[0] - graphNode = buildNode(tmp[1:], graphNode) + pkgTrie.Put(strings.ReplaceAll(nodeKey, ".", "/")) } - return graphNode + return pkgTrie } diff --git a/pkg/application/arch/tequila/incl_viz_test.go b/pkg/application/arch/tequila/incl_viz_test.go index a897f18f065b1aecf45f282801a9468c200dbda1..9e1999c1671b95b038ffb736f47ec9390b8cfb8d 100644 --- a/pkg/application/arch/tequila/incl_viz_test.go +++ b/pkg/application/arch/tequila/incl_viz_test.go @@ -2,13 +2,15 @@ package tequila import ( . "github.com/onsi/gomega" + "github.com/phodal/coca/cmd/cmd_util" + "github.com/phodal/coca/pkg/application/arch/tequila/trie" "testing" ) -func createBasicMap() (*GraphNode, *FullGraph) { +func createBasicMap() (*trie.PathTrie, *FullGraph) { fullGraph, nodeFilter := createGraph() - node := fullGraph.BuildMapTree(".", nodeFilter) + node := fullGraph.BuildMapTree(nodeFilter) return node, fullGraph } @@ -40,30 +42,29 @@ func Test_BuildGraphNode(t *testing.T) { g := NewGomegaWithT(t) node, _ := createBasicMap() - g.Expect(node.text).To(Equal("com")) - children := node.children - g.Expect(len(children)).To(Equal(2)) + g.Expect(len(node.Children["com"].Children)).To(Equal(2)) } func Test_ShouldMergeSameMap(t *testing.T) { g := NewGomegaWithT(t) fullGraph, nodeFilter := createGraph() fullGraph.NodeList["com.phodal.coca"] = "com.phodal.coca" - node := fullGraph.BuildMapTree(".", nodeFilter) + node := fullGraph.BuildMapTree(nodeFilter) - g.Expect(node.text).To(Equal("com")) - children := node.children - g.Expect(len(children)).To(Equal(3)) + g.Expect(len(node.Children["com"].Children)).To(Equal(2)) } func Test_BuildNodeDot(t *testing.T) { g := NewGomegaWithT(t) - node, graph := createBasicMap() + graph, nodeFilter := createGraph() + graph.NodeList["com.phodal.coca"] = "com.phodal.coca" + node := graph.BuildMapTree(nodeFilter) + dot := graph.ToMapDot(node) - //result := dot.String() - //cmd_util.WriteToCocaFile("demo.dot", result) + result := dot.String() + cmd_util.WriteToCocaFile("demo.dot", result) - g.Expect(len(dot.SubGraphs.SubGraphs)).To(Equal(5)) + g.Expect(len(dot.SubGraphs.SubGraphs)).To(Equal(6)) g.Expect(len(dot.Nodes.Nodes)).To(Equal(2)) } diff --git a/pkg/application/arch/tequila/trie/common.go b/pkg/application/arch/tequila/trie/common.go new file mode 100644 index 0000000000000000000000000000000000000000..d0715043862484a5401b211fb754a995c2a17012 --- /dev/null +++ b/pkg/application/arch/tequila/trie/common.go @@ -0,0 +1,53 @@ +// https://github.com/dghubble/trie +//The MIT License (MIT) +// +//Copyright (c) 2014 Dalton Hubble +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in +//all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +//THE SOFTWARE. + +package trie + +import ( + "strings" +) + +// WalkFunc defines some action to take on the given key and Value during +// a Trie Walk. Returning a non-nil error will terminate the Walk. +type WalkFunc func(key string, value interface{}) error + +// StringSegmenter takes a string key with a starting index and returns +// the first segment after the start and the ending index. When the end is +// reached, the returned nextIndex should be -1. +// Implementations should NOT allocate heap memory as Trie Segmenters are +// called upon Gets. See PathSegmenter. +type StringSegmenter func(key string, start int) (segment string, nextIndex int) + +// PathSegmenter segments string key paths by slash separators. For example, +// "/a/b/c" -> ("/a", 2), ("/b", 4), ("/c", -1) in successive calls. It does +// not allocate any heap memory. +func PathSegmenter(path string, start int) (segment string, next int) { + if len(path) == 0 || start < 0 || start > len(path)-1 { + return "", -1 + } + end := strings.IndexRune(path[start+1:], '/') // next '/' after 0th rune + if end == -1 { + return path[start:], -1 + } + return path[start : start+end+1], start + end + 1 +} diff --git a/pkg/application/arch/tequila/trie/path_trie.go b/pkg/application/arch/tequila/trie/path_trie.go new file mode 100644 index 0000000000000000000000000000000000000000..b10d77386e84888136bf6afe1c883d346f25d618 --- /dev/null +++ b/pkg/application/arch/tequila/trie/path_trie.go @@ -0,0 +1,76 @@ +// https://github.com/dghubble/trie +//The MIT License (MIT) +// +//Copyright (c) 2014 Dalton Hubble +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in +//all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +//THE SOFTWARE. + +package trie + +import "strings" + +type PathTrie struct { + segmenter StringSegmenter // key segmenter, must not cause heap allocs + Value string + Children map[string]*PathTrie +} + +// PathTrieConfig for building a path trie with different segmenter +type PathTrieConfig struct { + Segmenter StringSegmenter +} + +// NewPathTrie allocates and returns a new *PathTrie. +func NewPathTrie() *PathTrie { + return &PathTrie{ + segmenter: PathSegmenter, + } +} + +// NewPathTrieWithConfig allocates and returns a new *PathTrie with the given *PathTrieConfig +func NewPathTrieWithConfig(config *PathTrieConfig) *PathTrie { + segmenter := PathSegmenter + if config != nil && config.Segmenter != nil { + segmenter = config.Segmenter + } + + return &PathTrie{ + segmenter: segmenter, + } +} + +func (trie *PathTrie) Put(key string) { + node := trie + for part, i := trie.segmenter(key, 0); part != ""; part, i = trie.segmenter(key, i) { + child, _ := node.Children[part] + if child == nil { + if node.Children == nil { + node.Children = map[string]*PathTrie{} + } + child = NewPathTrie() + node.Children[part] = child + } + + child.Value = strings.ReplaceAll(part, "/", "") + node = child + } + // does node have an existing Value? + //isNewVal := node.Value == nil + //return isNewVal +}