forked from t-yuki/godoc2puml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
puml.go
157 lines (136 loc) · 3.35 KB
/
puml.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package printer
import (
"bytes"
"fmt"
"io"
"text/template"
"github.com/t-yuki/godoc2puml/ast"
)
var pumlTemplate = template.Must(template.New("plantuml").Funcs(pumlFuncs).Parse(`
@startuml
set namespaceSeparator /
{{- $lolipop := .Lolipop }}
{{- range $p := .Packages -}}
{{- range .Classes }}
class {{ joinName $p.Name .Name }} {
{{- range .Fields }}
{{ if .Public }}+{{ else }}~{{ end }}{{ .Name }} {{ .Type -}}
{{- end }}
{{- range .Methods }}
{{ if .Public }}+{{ else }}~{{ end }}{{ .Name }}({{ methodArgs .Arguments }})
{{- if .Results }} {{ methodResults .Results }}{{ end }}
{{- end }}
}
{{- end }}
{{- range .Interfaces }}
interface {{ joinName $p.Name .Name }} {
{{- range .Methods }}
{{ if .Public }}+{{ else }}~{{ end }}{{ .Name }}({{ methodArgs .Arguments }})
{{- if .Results }} {{ methodResults .Results }}{{ end }}
{{- end }}
}
{{- end }}
{{- range $cl := .Classes }}
{{- range .Relations }}
"{{ joinName $p.Name $cl.Name }}" {{ relType .RelType (isLolipop $lolipop .Target) }}
{{- if .Multiplicity }} "{{- .Multiplicity }}" {{ end }} "{{ qualifiedName .Target }}"
{{- if .Label }}: {{ .Label }}{{ end -}}
{{- end }}
{{- end }}
{{- range $iface := .Interfaces }}
{{- range .Relations }}
"{{ joinName $p.Name $iface.Name }}" {{ relType .RelType false }} "{{ qualifiedName .Target -}}"
{{- end }}
{{- end }}
{{- end }}
hide interface fields
@enduml
`))
var pumlFuncs = map[string]interface{}{
"relType": pumlRelType,
"methodArgs": pumlMethodArgs,
"methodResults": pumlMethodResults,
"qualifiedName": pumlQualifiedName,
"joinName": pumlJoinName,
"isLolipop": pumlIsLolipop,
}
func FprintPlantUML(w io.Writer, scope *ast.Scope, lolipopPackages []string) {
packages := make(ast.PackageSlice, 0, len(scope.Packages))
for _, p := range scope.Packages {
packages = append(packages, p)
}
packages.Sort()
err := pumlTemplate.Execute(w, map[string]interface{}{"Packages": packages, "Lolipop": lolipopPackages})
if err != nil {
panic(err)
}
}
func pumlRelType(relType ast.RelationType, lolipop bool) string {
switch relType {
case ast.Association:
return "->"
case ast.Extension:
return "-|>"
case ast.Composition:
return "*-"
case ast.Agregation:
return "o-"
case ast.Implementation:
if lolipop {
return "-()"
}
return ".|>"
}
panic(relType)
}
func pumlMethodArgs(decls []ast.DeclPair) string {
b := &bytes.Buffer{}
for i, v := range decls {
if i != 0 {
b.WriteString(", ")
}
if v.Name == "" {
fmt.Fprintf(b, "%s", v.Type)
} else {
fmt.Fprintf(b, "%s %s", v.Name, v.Type)
}
}
return b.String()
}
func pumlMethodResults(decls []ast.DeclPair) string {
if len(decls) >= 2 {
return "(" + pumlMethodArgs(decls) + ")"
}
return pumlMethodArgs(decls)
}
func pumlQualifiedName(name string) string {
for i := len(name) - 1; i >= 0 && name[i] != '/'; i-- {
if name[i] == '.' {
return name[:i] + "/" + name[i+1:]
}
}
return name
}
func pumlJoinName(name1 string, name2 string) string {
if name1 == "" {
return name2
}
if name2 == "" {
return name1
}
return name1 + "/" + name2
}
func pumlIsLolipop(packages []string, name string) bool {
actual := ""
for i := len(name) - 1; i >= 0 && name[i] != '/'; i-- {
if name[i] == '.' {
actual = name[:i]
}
}
for _, pkg := range packages {
if pkg == actual {
return true
}
}
return false
}