Ai
1 Star 0 Fork 0

HaRo/go-package-plantuml

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
codeanalysis.go 27.38 KB
一键复制 编辑 原始数据 按行查看 历史
lianglongqun 提交于 2017-04-16 18:54 +08:00 . demo
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229
package codeanalysis
import (
"go/parser"
"path/filepath"
"os"
"strings"
"go/token"
"reflect"
"go/ast"
log "github.com/Sirupsen/logrus"
"io/ioutil"
"fmt"
"path"
"encoding/json"
)
type Config struct {
CodeDir string
GopathDir string
VendorDir string
IgnoreDirs []string
}
type AnalysisResult interface {
OutputToFile(logfile string)
}
func AnalysisCode(config Config) AnalysisResult {
tool := &analysisTool{
interfaceMetas : []*interfaceMeta{},
structMetas : []*structMeta{},
typeAliasMetas : []*typeAliasMeta{},
packagePathPackageNameCache : map[string]string{},
dependencyRelations : []*DependencyRelation{},
}
tool.analysis(config)
return tool
}
func HasPrefixInSomeElement(value string, src []string) bool {
result := false
for _, srcValue := range src {
if (strings.HasPrefix(value, srcValue)) {
result = true
break
}
}
return result
}
func sliceContains(src []string, value string) bool {
isContain := false
for _, srcValue := range src {
if (srcValue == value) {
isContain = true
break
}
}
return isContain
}
func sliceContainsSlice(s []string, s2 []string) bool {
for _, str := range s2 {
if ! sliceContains(s, str) {
return false
}
}
return true
}
func mapContains(src map[string]string, key string) bool {
if _, ok := src[key]; ok {
return true
}
return false
}
func findGoPackageNameInDirPath(dirpath string) string {
dir_list, e := ioutil.ReadDir(dirpath)
if e != nil {
fmt.Errorf("读取目录%s文件列表失败,%s", dirpath, e)
return ""
}
for _, fileInfo := range dir_list {
if ! fileInfo.IsDir() && strings.HasSuffix(fileInfo.Name(), ".go") {
packageName := ParsePackageNameFromGoFile(path.Join(dirpath, fileInfo.Name()))
if packageName != "" {
return packageName
}
}
}
return ""
}
func ParsePackageNameFromGoFile(filepath string) string {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, filepath, nil, parser.ParseComments)
if err != nil {
log.Errorf("解析文件%s失败, %s", filepath, err)
return ""
}
return file.Name.Name
}
func PathExists(path string) (bool) {
_, err := os.Stat(path)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return false
}
func packagePathToUML(packagePath string) (string) {
packagePath = strings.Replace(packagePath, "/", "\\\\", -1)
packagePath = strings.Replace(packagePath, "-", "_", -1)
return packagePath
}
type baseInfo struct {
// go文件路径
FilePath string
// 包路径, 例如 git.oschina.net/jscode/list-interface
PackagePath string
}
type interfaceMeta struct {
baseInfo
Name string
// interface的方法签名列表,
MethodSigns []string
// UML图节点
UML string
}
func (this *interfaceMeta) UniqueNameUML() string {
return packagePathToUML(this.PackagePath) + "." + this.Name
}
type structMeta struct {
baseInfo
Name string
// struct的方法签名列表
MethodSigns []string
// UML图节点
UML string
}
type typeAliasMeta struct {
baseInfo
Name string
targetTypeName string
}
func (this *structMeta) UniqueNameUML() string {
return packagePathToUML(this.PackagePath) + "." + this.Name
}
func (this *structMeta) implInterfaceUML(interfaceMeta1 *interfaceMeta) string {
return fmt.Sprintf("%s <|- %s\n", interfaceMeta1.UniqueNameUML(), this.UniqueNameUML())
}
type importMeta struct {
// 例如 main
Alias string
// 例如 git.oschina.net/jscode/list-interface
Path string
}
type DependencyRelation struct {
source *structMeta
target *structMeta
uml string
}
type analysisTool struct {
config Config
// 当前解析的go文件, 例如/appdev/go-demo/src/git.oschina.net/jscode/list-interface/a.go
currentFile string
// 当前解析的go文件,所在包路径, 例如git.oschina.net/jscode/list-interface
currentPackagePath string
// 当前解析的go文件,引入的其他包
currentFileImports []*importMeta
// 所有的interface
interfaceMetas []*interfaceMeta
// 所有的struct
structMetas []*structMeta
// 所有的别名定义
typeAliasMetas []*typeAliasMeta
// package path与package name的映射关系,例如git.oschina.net/jscode/list-interface 对应的pakcage name为 main
packagePathPackageNameCache map[string]string
// struct之间的依赖关系
dependencyRelations []*DependencyRelation
}
func (this *analysisTool)analysis(config Config) {
this.config = config
if this.config.CodeDir == "" || ! PathExists(this.config.CodeDir) {
log.Errorf("找不到代码目录%s\n", this.config.CodeDir)
return
}
if this.config.GopathDir == "" || ! PathExists(this.config.GopathDir) {
log.Errorf("找不到GOPATH目录%s\n", this.config.GopathDir)
return
}
for _, lib := range stdlibs {
this.mapPackagePath_PackageName(lib, path.Base(lib))
}
dir_walk_once := func(path string, info os.FileInfo, err error) error {
// 过滤掉测试代码
if strings.HasSuffix(path, ".go") && ! strings.HasSuffix(path, "test.go") {
if config.IgnoreDirs != nil && HasPrefixInSomeElement(path, config.IgnoreDirs) {
// ignore
} else {
log.Info("解析 " + path)
this.visitTypeInFile(path)
}
}
return nil
}
filepath.Walk(config.CodeDir, dir_walk_once)
dir_walk_twice := func(path string, info os.FileInfo, err error) error {
// 过滤掉测试代码
if strings.HasSuffix(path, ".go") && ! strings.HasSuffix(path, "test.go") {
if config.IgnoreDirs != nil && HasPrefixInSomeElement(path, config.IgnoreDirs) {
// ignore
} else {
log.Info("解析 " + path)
this.visitFuncInFile(path)
}
}
return nil
}
filepath.Walk(config.CodeDir, dir_walk_twice)
}
func (this *analysisTool) initFile(path string) {
log.Debug("path=", path)
this.currentFile = path
this.currentPackagePath = this.filepathToPackagePath(path)
if this.currentPackagePath == "" {
log.Errorf("packagePath为空,currentFile=%s\n", this.currentFile)
}
}
func (this *analysisTool) mapPackagePath_PackageName(packagePath string, packageName string) {
if packagePath == "" || packageName == "" {
log.Errorf("mapPackagePath_PackageName, packageName=%s, packagePath=%s\n, current_file=%s",
packageName, packagePath, this.currentFile)
return
}
if mapContains(this.packagePathPackageNameCache, packagePath) {
return
}
log.Debugf("mapPackagePath_PackageName, packageName=%s, packagePath=%s\n", packageName, packagePath)
this.packagePathPackageNameCache[packagePath] = packageName
}
func (this *analysisTool) visitTypeInFile(path string) {
this.initFile(path)
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
log.Fatal(err)
return
}
this.mapPackagePath_PackageName(this.currentPackagePath, file.Name.Name)
for _, decl := range file.Decls {
genDecl, ok := decl.(*ast.GenDecl)
if ok {
for _, spec := range genDecl.Specs {
typeSpec, ok := spec.(*ast.TypeSpec)
if ok {
this.visitTypeSpec(typeSpec)
}
}
}
}
}
func (this *analysisTool) visitTypeSpec(typeSpec *ast.TypeSpec) {
interfaceType, ok := typeSpec.Type.(*ast.InterfaceType)
if ok {
this.visitInterfaceType(typeSpec.Name.Name, interfaceType)
return
}
structType, ok := typeSpec.Type.(*ast.StructType)
if ok {
this.visitStructType(typeSpec.Name.Name, structType)
return
}
// 其他类型别名
this.typeAliasMetas = append(this.typeAliasMetas, &typeAliasMeta{
baseInfo : baseInfo{
FilePath : this.currentFile,
PackagePath : this.currentPackagePath,
},
Name : typeSpec.Name.Name,
targetTypeName: "",
})
}
func (this*analysisTool) filepathToPackagePath(filepath string) string {
filepath = path.Dir(filepath)
if this.config.VendorDir != "" {
if (strings.HasPrefix(filepath, this.config.VendorDir)) {
packagePath := strings.TrimPrefix(filepath, this.config.VendorDir)
packagePath = strings.TrimPrefix(packagePath, "/")
return packagePath
}
}
if this.config.GopathDir != "" {
srcdir := path.Join(this.config.GopathDir, "src")
if strings.HasPrefix(filepath, srcdir) {
packagePath := strings.TrimPrefix(filepath, srcdir)
packagePath = strings.TrimPrefix(packagePath, "/")
return packagePath
}
}
log.Errorf("无法确认包路径名, filepath=%s\n", filepath)
return ""
}
func (this *analysisTool) visitFuncInFile(path string) {
this.initFile(path)
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
log.Fatal(err)
return
}
this.currentFileImports = []*importMeta{}
if file.Imports != nil {
for _, import1 := range file.Imports {
alias := ""
packagePath := strings.TrimSuffix(strings.TrimPrefix(import1.Path.Value, "\""), "\"")
if import1.Name != nil {
alias = import1.Name.Name
} else {
aliasCache, ok := this.packagePathPackageNameCache[packagePath]
log.Debugf("findAliasInCache,packagePath=%s,alias=%s,ok=%t\n", packagePath, aliasCache, ok)
if ok {
alias = aliasCache
} else {
alias = this.findAliasByPackagePath(packagePath)
}
}
log.Debugf("current_file=%s packagePath=%s, alias=%s\n", this.currentFile, packagePath, alias)
this.currentFileImports = append(this.currentFileImports, &importMeta{
Alias : alias,
Path:packagePath,
})
}
}
for _, decl := range file.Decls {
genDecl, ok := decl.(*ast.GenDecl)
if ok {
for _, spec := range genDecl.Specs {
typeSpec, ok := spec.(*ast.TypeSpec)
if ok {
interfaceType, ok := typeSpec.Type.(*ast.InterfaceType)
if ok {
this.visitInterfaceFunctions(typeSpec.Name.Name, interfaceType)
}
structType, ok := typeSpec.Type.(*ast.StructType)
if ok {
this.visitStructFields(typeSpec.Name.Name, structType)
}
}
}
}
}
for _, decl := range file.Decls {
funcDecl, ok := decl.(*ast.FuncDecl)
if ok {
this.visitFunc(funcDecl)
}
}
}
func (this *analysisTool) visitStructType(name string, structType *ast.StructType) {
strutMeta1 := &structMeta{
baseInfo : baseInfo{
FilePath:this.currentFile,
PackagePath:this.currentPackagePath,
},
Name : name,
MethodSigns:[]string{},
}
this.structMetas = append(this.structMetas, strutMeta1)
}
func (this *analysisTool) visitStructFields(structName string, structType *ast.StructType) {
sourceStruct1 := this.findStruct(this.currentPackagePath, structName)
sourceStruct1.UML = this.structToUML(structName, structType)
for _, field := range structType.Fields.List {
this.visitStructField(sourceStruct1, field)
}
}
func (this *analysisTool) visitStructField(sourceStruct1 *structMeta, field *ast.Field) {
fieldNames := this.IdentsToString(field.Names)
targetStruct1, isarray := this.analysisTypeForDependencyRelation(field.Type)
if targetStruct1 != nil {
if fieldNames == "" {
d := DependencyRelation{
source: sourceStruct1,
target:targetStruct1,
uml : sourceStruct1.UniqueNameUML() + " -|> " + targetStruct1.UniqueNameUML(),
}
this.dependencyRelations = append(this.dependencyRelations, &d)
} else {
if isarray {
d := DependencyRelation{
source: sourceStruct1,
target:targetStruct1,
uml : sourceStruct1.UniqueNameUML() + " ---> \"*\" " + targetStruct1.UniqueNameUML() + " : " + fieldNames,
}
this.dependencyRelations = append(this.dependencyRelations, &d)
} else {
d := DependencyRelation{
source: sourceStruct1,
target:targetStruct1,
uml : sourceStruct1.UniqueNameUML() + " ---> " + targetStruct1.UniqueNameUML() + " : " + fieldNames,
}
this.dependencyRelations = append(this.dependencyRelations, &d)
}
}
}
}
func (this *analysisTool) isGoBaseType(type1 string) bool {
baseTypes := []string{"bool", "byte", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64",
"float32", "float64", "complex64", "complex128", "string", "uintptr", "rune", "error"}
if sliceContains(baseTypes, type1) {
return true
}
return false
}
func (this *analysisTool) findStructByAliasAndStructName(alias string, structName string) (*structMeta) {
if alias == "" && this.isGoBaseType(structName) {
return nil
}
packagepath := this.findPackagePathByAlias(alias, structName)
if packagepath != "" {
return this.findStruct(packagepath, structName)
}
return nil
}
func (this *analysisTool) analysisTypeForDependencyRelation(t ast.Expr) (structMeta1 *structMeta, isArray bool) {
structMeta1 = nil
isArray = false
ident, ok := t.(*ast.Ident)
if ok {
structMeta1 = this.findStructByAliasAndStructName("", ident.Name)
isArray = false
return
}
starExpr, ok := t.(*ast.StarExpr)
if ok {
structMeta1, isArray = this.analysisTypeForDependencyRelation(starExpr.X)
return
}
arrayType, ok := t.(*ast.ArrayType)
if ok {
eleStructName, _ := this.analysisTypeForDependencyRelation(arrayType.Elt)
structMeta1 = eleStructName
isArray = true
return
}
mapType, ok := t.(*ast.MapType)
if ok {
valueStructMeta1, _ := this.analysisTypeForDependencyRelation(mapType.Value)
structMeta1 = valueStructMeta1
isArray = true
return
}
selectorExpr, ok := t.(*ast.SelectorExpr)
if ok {
alias := this.typeToString(selectorExpr.X, false)
structMeta1 = this.findStructByAliasAndStructName(alias, this.typeToString(selectorExpr.Sel, false))
isArray = false
return
}
return
}
func (this *analysisTool) structToUML(name string, structType *ast.StructType) string {
classUML := "class " + name + " " + this.structBodyToString(structType)
return fmt.Sprintf("namespace %s {\n %s \n}", this.packagePathToUML(this.currentPackagePath), classUML)
}
func (this *analysisTool) packagePathToUML(packagePath string) string {
return packagePathToUML(packagePath)
}
func (this *analysisTool) structBodyToString(structType *ast.StructType) string {
result := "{\n"
for _, field := range structType.Fields.List {
result += " " + this.fieldToString(field) + "\n"
}
result += "}"
return result
}
func (this *analysisTool) visitInterfaceType(name string, interfaceType *ast.InterfaceType) {
interfaceInfo1 := &interfaceMeta{
baseInfo : baseInfo{
FilePath : this.currentFile,
PackagePath : this.currentPackagePath,
},
Name:name,
}
this.interfaceMetas = append(this.interfaceMetas, interfaceInfo1)
}
func (this *analysisTool) interfaceToUML(name string, interfaceType *ast.InterfaceType) string {
interfaceUML := "interface " + name + " " + this.interfaceBodyToString(interfaceType)
return fmt.Sprintf("namespace %s {\n %s \n}", this.packagePathToUML(this.currentPackagePath), interfaceUML)
}
func (this *analysisTool) funcParamsResultsToString(funcType *ast.FuncType) string {
funcString := "("
if funcType.Params != nil {
for index, field := range funcType.Params.List {
if index != 0 {
funcString += ","
}
funcString += this.fieldToString(field)
}
}
funcString += ")"
if funcType.Results != nil {
if len(funcType.Results.List) >= 2 {
funcString += "("
}
for index, field := range funcType.Results.List {
if index != 0 {
funcString += ","
}
funcString += this.fieldToString(field)
}
if len(funcType.Results.List) >= 2 {
funcString += ")"
}
}
return funcString
}
func (this*analysisTool) findStruct(packagePath string, structName string) *structMeta {
for _, structMeta1 := range this.structMetas {
if structMeta1.Name == structName && structMeta1.PackagePath == packagePath {
return structMeta1
}
}
return nil
}
func (this*analysisTool) findTypeAlias(packagePath string, structName string) *typeAliasMeta {
for _, typeAliasMeta1 := range this.typeAliasMetas {
if typeAliasMeta1.Name == structName && typeAliasMeta1.PackagePath == packagePath {
return typeAliasMeta1
}
}
return nil
}
func (this*analysisTool) findInterfaceMeta(packagePath string, interfaceName string) *interfaceMeta {
for _, interfaceMeta := range this.interfaceMetas {
if interfaceMeta.Name == interfaceName && interfaceMeta.PackagePath == packagePath {
return interfaceMeta
}
}
return nil
}
func (this *analysisTool) visitFunc(funcDecl *ast.FuncDecl) {
this.debugFunc(funcDecl)
packageAlias, structName := this.findStructTypeOfFunc(funcDecl)
if structName != "" {
packagePath := ""
if packageAlias == "" {
packagePath = this.currentPackagePath
}
structMeta := this.findStruct(packagePath, structName)
if structMeta != nil {
methodSign := this.createMethodSign(funcDecl.Name.Name, funcDecl.Type)
structMeta.MethodSigns = append(structMeta.MethodSigns, methodSign)
}
}
}
func (this *analysisTool) visitInterfaceFunctions(name string, interfaceType *ast.InterfaceType) {
methods := []string{}
for _, field := range interfaceType.Methods.List {
funcType, ok := field.Type.(*ast.FuncType)
if ok {
methods = append(methods, this.createMethodSign(field.Names[0].Name, funcType))
}
}
interfaceMeta := this.findInterfaceMeta(this.currentPackagePath, name)
interfaceMeta.MethodSigns = methods
interfaceMeta.UML = this.interfaceToUML(name, interfaceType)
}
func (this *analysisTool) findStructTypeOfFunc(funcDecl *ast.FuncDecl) (packageAlias string, structName string) {
if funcDecl.Recv != nil {
for _, field := range funcDecl.Recv.List {
t := field.Type
ident, ok := t.(*ast.Ident)
if ok {
packageAlias = ""
structName = ident.Name
}
starExpr, ok := t.(*ast.StarExpr)
if ok {
ident, ok := starExpr.X.(*ast.Ident)
if ok {
packageAlias = ""
structName = ident.Name
}
}
}
}
return
}
func (this *analysisTool) debugFunc(funcDecl *ast.FuncDecl) {
log.Debug("func name=", funcDecl.Name)
if funcDecl.Recv != nil {
for _, field := range funcDecl.Recv.List {
log.Debug("func recv, name=", field.Names, " type=", field.Type)
}
}
if funcDecl.Type.Params != nil {
for _, field := range funcDecl.Type.Params.List {
log.Debug("func param, name=", field.Names, " type=", field.Type)
}
}
if funcDecl.Type.Results != nil {
for _, field := range funcDecl.Type.Results.List {
log.Debug("func result, type=", field.Type)
}
}
}
func (this *analysisTool) IdentsToString(names []*ast.Ident) string {
r := ""
for index, name := range names {
if index != 0 {
r += ","
}
r += name.Name
}
return r
}
// 创建方法签名
func (this *analysisTool) createMethodSign(methodName string, funcType *ast.FuncType) string {
methodSign := methodName + "("
if funcType.Params != nil {
for index, field := range funcType.Params.List {
if index != 0 {
methodSign += ","
}
methodSign += this.fieldToStringInMethodSign(field)
}
}
methodSign += ")"
if funcType.Results != nil {
if len(funcType.Results.List) >= 2 {
methodSign += "("
}
for index, field := range funcType.Results.List {
if index != 0 {
methodSign += ","
}
methodSign += this.fieldToStringInMethodSign(field)
}
if len(funcType.Results.List) >= 2 {
methodSign += ")"
}
}
return methodSign
}
func (this *analysisTool) fieldToStringInMethodSign(f *ast.Field) string {
argCount := len(f.Names)
if argCount == 0 {
argCount = 1
}
sign := ""
for i := 0; i < argCount; i++ {
if i != 0 {
sign += ","
}
sign += this.typeToString(f.Type, true)
}
return sign
}
func (this *analysisTool) fieldToString(f *ast.Field) string {
r := ""
if len(f.Names) > 0 {
for index, name := range f.Names {
if index != 0 {
r += ","
}
r += name.Name
}
r += " "
}
r += this.typeToString(f.Type, false)
return r;
}
func (this *analysisTool) typeToString(t ast.Expr, convertTypeToUnqiueType bool) (string) {
ident, ok := t.(*ast.Ident)
if ok {
if convertTypeToUnqiueType {
return this.addPackagePathWhenStruct(ident.Name)
} else {
return ident.Name
}
}
starExpr, ok := t.(*ast.StarExpr)
if ok {
return "*" + this.typeToString(starExpr.X, convertTypeToUnqiueType)
}
arrayType, ok := t.(*ast.ArrayType)
if ok {
return "[]" + this.typeToString(arrayType.Elt, convertTypeToUnqiueType)
}
mapType, ok := t.(*ast.MapType)
if ok {
return "map[" + this.typeToString(mapType.Key, convertTypeToUnqiueType) + "]" + this.typeToString(mapType.Value, convertTypeToUnqiueType)
}
chanType, ok := t.(*ast.ChanType)
if ok {
return "chan " + this.typeToString(chanType.Value, convertTypeToUnqiueType)
}
funcType, ok := t.(*ast.FuncType)
if ok {
return "func" + this.funcParamsResultsToString(funcType)
}
interfaceType, ok := t.(*ast.InterfaceType)
if ok {
return "interface " + strings.Replace(this.interfaceBodyToString(interfaceType), "\n", " ", -1)
}
selectorExpr, ok := t.(*ast.SelectorExpr)
if ok {
if convertTypeToUnqiueType {
return this.findPackagePathByAlias(this.selectorExprToString(selectorExpr.X), selectorExpr.Sel.Name) + "." + selectorExpr.Sel.Name
} else {
return this.typeToString(selectorExpr.X, true) + "." + selectorExpr.Sel.Name
}
}
structType, ok := t.(*ast.StructType)
if ok {
return "struct " + strings.Replace(this.structBodyToString(structType), "\n", " ", -1)
}
ellipsis, ok := t.(*ast.Ellipsis)
if ok {
return "... " + this.typeToString(ellipsis.Elt, convertTypeToUnqiueType)
}
parenExpr, ok := t.(*ast.ParenExpr)
if ok {
return " (" + this.typeToString(parenExpr.X, convertTypeToUnqiueType) + ")"
}
log.Error("typeToString ", reflect.TypeOf(t), " file=", this.currentFile, " expr=", this.content(t))
return ""
}
func (this *analysisTool) selectorExprToString(t ast.Expr) (string) {
ident, ok := t.(*ast.Ident)
if ok {
return ident.Name
}
log.Error("selectorExprToString ", reflect.TypeOf(t), " file=", this.currentFile, " expr=", this.content(t))
return ""
}
func (this *analysisTool) addPackagePathWhenStruct(fieldType string) string {
searchPackages := []string{this.currentPackagePath}
for _, import1 := range this.currentFileImports {
if import1.Alias == "." {
searchPackages = append(searchPackages, import1.Path)
}
}
for _, meta := range this.structMetas {
if sliceContains(searchPackages, meta.PackagePath) && meta.Name == fieldType {
return meta.PackagePath + "." + fieldType
}
}
for _, meta := range this.interfaceMetas {
if sliceContains(searchPackages, meta.PackagePath) && meta.Name == fieldType {
return meta.PackagePath + "." + fieldType
}
}
return fieldType
}
func (this *analysisTool) findAliasByPackagePath(packagePath string) string {
result := ""
if this.config.VendorDir != "" {
absPath := path.Join(this.config.VendorDir, packagePath)
if PathExists(absPath) {
result = findGoPackageNameInDirPath(absPath)
}
}
if this.config.GopathDir != "" {
absPath := path.Join(this.config.GopathDir, "src", packagePath)
if PathExists(absPath) {
result = findGoPackageNameInDirPath(absPath)
}
}
log.Debugf("packagepath=%s, alias=%s\n", packagePath, result)
return result
}
func (this*analysisTool) existStructOrInterfaceInPackage(typeName string, packageName string) bool {
structMeta1 := this.findStruct(this.currentPackagePath, typeName)
if structMeta1 != nil {
return true
}
interfaceMeta1 := this.findInterfaceMeta(this.currentPackagePath, typeName)
if interfaceMeta1 != nil {
return true
}
return false
}
func (this*analysisTool) existTypeAliasInPackage(typeName string, packageName string) bool {
meta1 := this.findTypeAlias(this.currentPackagePath, typeName)
if meta1 != nil {
return true
}
return false
}
func (this *analysisTool) findPackagePathByAlias(alias string, structName string) string {
if alias == "" {
if this.existStructOrInterfaceInPackage(structName, this.currentPackagePath) {
return this.currentPackagePath
}
if this.existTypeAliasInPackage(structName, this.currentPackagePath) {
// 忽略别名类型
return ""
}
matchedImportMetas := []*importMeta{}
for _, importMeta := range this.currentFileImports {
if importMeta.Alias == "." {
matchedImportMetas = append(matchedImportMetas, importMeta)
}
}
if len(matchedImportMetas) > 1 {
for _, matchedImportMeta := range matchedImportMetas {
if this.existStructOrInterfaceInPackage(structName, matchedImportMeta.Path) {
log.Debugf("findPackagePathByAlias, alias=%s, packagePath=%s\n", alias, matchedImportMeta.Path)
return matchedImportMeta.Path
}
if this.existTypeAliasInPackage(structName, matchedImportMeta.Path) {
// 忽略别名类型
return ""
}
}
}
currentFileImportsjson, _ := json.Marshal(this.currentFileImports)
log.Warnf("找不到包的全路径,包名为%s,type name=%s, 在%s文件, matchedImportMetas=%d, currentFileImports=%s", alias, structName, this.currentFile, len(matchedImportMetas), currentFileImportsjson)
return alias
} else {
for _, importMeta := range this.currentFileImports {
if importMeta.Path == alias {
log.Debugf("findPackagePathByAlias, alias=%s, packagePath=%s\n", alias, alias)
return alias
}
}
matchedImportMetas := []*importMeta{}
for _, importMeta := range this.currentFileImports {
if importMeta.Alias == alias {
matchedImportMetas = append(matchedImportMetas, importMeta)
}
}
if len(matchedImportMetas) == 1 {
log.Debugf("findPackagePathByAlias, alias=%s, packagePath=%s\n", alias, matchedImportMetas[0].Path)
return matchedImportMetas[0].Path
}
if len(matchedImportMetas) > 1 {
for _, matchedImportMeta := range matchedImportMetas {
if this.existStructOrInterfaceInPackage(structName, matchedImportMeta.Path) {
log.Debugf("findPackagePathByAlias, alias=%s, packagePath=%s\n", alias, matchedImportMeta.Path)
return matchedImportMeta.Path
}
if this.existTypeAliasInPackage(structName, matchedImportMeta.Path) {
// 忽略别名类型
return ""
}
}
}
currentFileImportsjson, _ := json.Marshal(this.currentFileImports)
log.Warnf("找不到包的全路径,包名为%s,type name=%s, 在%s文件, matchedImportMetas=%d, currentFileImports=%s", alias, structName, this.currentFile, len(matchedImportMetas), currentFileImportsjson)
return alias
}
}
func (this *analysisTool) interfaceBodyToString(interfaceType *ast.InterfaceType) string {
result := " {\n"
for _, field := range interfaceType.Methods.List {
funcType, ok := field.Type.(*ast.FuncType)
if ok {
result += " " + this.IdentsToString(field.Names) + this.funcParamsResultsToString(funcType) + "\n"
}
}
result += "}"
return result
}
func (this *analysisTool) content(t ast.Expr) string {
bytes, err := ioutil.ReadFile(this.currentFile)
if err != nil {
log.Error("读取文件", this.currentFile, "失败", err)
return ""
}
return string(bytes[t.Pos() - 1:t.End() - 1])
}
/**
* 查找interface有哪些实现的Struct
*/
func (this *analysisTool) findInterfaceImpls(interfaceMeta1 *interfaceMeta) []*structMeta {
metas := [] *structMeta{}
for _, structMeta1 := range this.structMetas {
if sliceContainsSlice(structMeta1.MethodSigns, interfaceMeta1.MethodSigns) {
metas = append(metas, structMeta1)
}
}
return metas
}
func (this *analysisTool) UML() string {
uml := ""
for _, structMeta1 := range this.structMetas {
uml += structMeta1.UML
uml += "\n"
}
for _, interfaceMeta1 := range this.interfaceMetas {
uml += interfaceMeta1.UML
uml += "\n"
}
for _, d := range this.dependencyRelations {
uml += d.uml
uml += "\n"
}
for _, interfaceMeta1 := range this.interfaceMetas {
structMetas := this.findInterfaceImpls(interfaceMeta1)
for _, structMeta := range structMetas {
uml += structMeta.implInterfaceUML(interfaceMeta1)
}
}
return "@startuml\n" + uml + "@enduml"
}
func (this*analysisTool) OutputToFile(logfile string) {
uml := this.UML()
ioutil.WriteFile(logfile, []byte(uml), 0666)
log.Infof("数据已保存到%s\n", logfile)
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/harogloire/go-package-plantuml.git
git@gitee.com:harogloire/go-package-plantuml.git
harogloire
go-package-plantuml
go-package-plantuml
cf5530c2d1c1

搜索帮助