pkg/application/call/call_graph.go
package call
import (
"github.com/modernizing/coca/pkg/application/rcall"
apidomain "github.com/modernizing/coca/pkg/domain/api_domain"
"github.com/modernizing/coca/pkg/domain/core_domain"
"github.com/modernizing/coca/pkg/infrastructure/jpackage"
"strings"
)
type CallGraph struct {
}
func NewCallGraph() CallGraph {
return CallGraph{}
}
func (c CallGraph) Analysis(funcName string, clzs []core_domain.CodeDataStruct, lookup bool) string {
methodMap := BuildMethodMap(clzs)
chain := BuildCallChain(funcName, methodMap, nil)
if lookup {
var projectMethodMap = rcall.BuildProjectMethodMap(clzs)
rcallMap := rcall.BuildMethodCallMap(clzs, projectMethodMap)
graph := rcall.NewRCallGraph()
rCallChain := graph.BuildRCallChain(funcName, rcallMap)
chain = chain + rCallChain
}
dotContent := ToGraphviz(chain)
return dotContent
}
// TODO: be a utils
func ToGraphviz(chain string) string {
// rankdir = LR;
var result = "digraph G {\n"
result += "rankdir = LR;\n"
result = result + chain
result = result + "}\n"
return result
}
var loopCount = 0
var maxLoopCount = 6
func BuildCallChain(funcName string, methodMap map[string][]string, diMap map[string]string) string {
if loopCount > maxLoopCount {
return "\n"
}
loopCount++
if len(methodMap[funcName]) > 0 {
var arrayResult = ""
for _, child := range methodMap[funcName] {
if _, ok := diMap[jpackage.GetClassName(child)]; ok {
child = diMap[jpackage.GetClassName(child)] + "." + jpackage.GetMethodName(child)
}
if len(methodMap[child]) > 0 {
arrayResult = arrayResult + BuildCallChain(child, methodMap, diMap)
}
arrayResult = arrayResult + "\"" + escapeStr(funcName) + "\" -> \"" + escapeStr(child) + "\";\n"
}
return arrayResult
}
return "\n"
}
func (c CallGraph) AnalysisByFiles(restApis []apidomain.RestAPI, deps []core_domain.CodeDataStruct, diMap map[string]string) (string, []apidomain.CallAPI) {
methodMap := BuildMethodMap(deps)
var apiCallSCounts []apidomain.CallAPI
results := "digraph G { \n"
for _, restApi := range restApis {
caller := restApi.BuildFullMethodPath()
loopCount = 0
chain := "\"" + restApi.HttpMethod + " " + restApi.Uri + "\" -> \"" + escapeStr(caller) + "\";\n"
apiCallChain := BuildCallChain(caller, methodMap, diMap)
chain = chain + apiCallChain
count := &apidomain.CallAPI{
HTTPMethod: restApi.HttpMethod,
Caller: caller,
URI: restApi.Uri,
Size: len(strings.Split(apiCallChain, " -> ")),
}
apiCallSCounts = append(apiCallSCounts, *count)
results = results + "\n" + chain
}
return results + "}\n", apiCallSCounts
}
func escapeStr(caller string) string {
return strings.ReplaceAll(caller, "\"", "\\\"")
}
func BuildMethodMap(structs []core_domain.CodeDataStruct) map[string][]string {
var methodMap = make(map[string][]string)
for _, clz := range structs {
for _, method := range clz.Functions {
methodName := method.BuildFullMethodName(clz)
methodMap[methodName] = method.GetAllCallString()
}
}
return methodMap
}