Skip to content
Snippets Groups Projects
Commit 1696367e authored by Tóth Miklós Tibor's avatar Tóth Miklós Tibor :shrug:
Browse files

class diagrams are fun

parent 1a2c6132
No related branches found
No related tags found
No related merge requests found
Pipeline #6294 failed
......@@ -18,15 +18,6 @@ go:
paths:
- plab/out/plab
#java:
# image: openjdk:11
# stage: build
# script:
# - cd robinbird
# - ./gradlew build
# artifacts:
# paths:
# - robinbird/build/distributions/robinbird.tar
Build:
stage: docker
......
[submodule "robinbird"]
path = robinbird
url = https://github.com/SeokhyunKim/robinbird.git
FROM texlive/texlive
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get -y upgrade && apt-get -y install openjdk-11-jdk-headless hunspell hunspell-hu hunspell-en-gb hunspell-en-us python3-pip && pip3 install javalang
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get -y upgrade && apt-get -y install inkscape plantuml openjdk-11-jdk-headless hunspell hunspell-hu hunspell-en-gb hunspell-en-us python3-pip && pip3 install javalang
COPY tikz-uml.sty /usr/local/texlive/2020/texmf-dist/tex/latex/tikz-uml/tikz-uml.sty
COPY jsonDoclet.jar /root/jsonDoclet.jar
COPY ./plab/out/plab /usr/bin/plab
#ADD robinbird/build/distributions/robinbird.tar /
RUN mktexlsr
COPY gen_seq_diag.py /
RUN chmod +x /gen_seq_diag.py
\ No newline at end of file
all: docker
all: podman
docker:
docker build -t projlab/projlab .
go_plab:
bash -c "cd plab; make podman"
podman: go_plab
podman build -t projlab/projlab .
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"regexp"
"strings"
)
var Classdiag = Subcommand{
Name: "classdiag",
Command: func(args []string) {
if len(args) > 1 {
fmt.Println("usage: classdiag [source location]")
return
}
if len(args) != 0 {
e := genJavadoc(args[0])
if e != nil {
panic(e)
}
}
classes, e := readJavadoc()
if e != nil {
panic(e)
}
classes = linkInheritance(classes)
classes = resolveDocsFromInheritance(classes)
makePlantUML(classes)
fakePipeline()
},
Help: "create and parse javadoc from code",
}
func makePlantUML(classes map[string]*Class) {
genPlantUMLStr(classes)
}
var arrowRegex = regexp.MustCompile(`(<|<\|)?[^0-9](--|\.\.)(>|\|>)?`)
const classFile = "/tmp/projlabclasses.txt"
func genPlantUMLStr(classes map[string]*Class) {
var output bytes.Buffer
output.WriteString(fmt.Sprintln("@startuml"))
output.WriteString(fmt.Sprintln("skinparam classAttributeIconSize 0"))
output.WriteString(fmt.Sprintln("set namespaceSeparator none"))
for _, c := range classes {
after := ""
if c.Superclass.realClass != nil {
output.WriteString(fmt.Sprintf("%s --|> %s\n", c.Name, c.Superclass.realClass.Name))
}
for _, i := range c.Interfaces {
output.WriteString(fmt.Sprintf("%s ..|> %s\n", c.Name, i.Name))
}
t := "class"
if strings.Contains(c.Modifiers, "interface") {
t = "interface"
}
if strings.Contains(c.Modifiers, "abstract") {
t = "abstract " + t
}
output.WriteString(fmt.Sprintf("%s %s {\n", t, c.Name))
for _, f := range c.Fields {
uml := false
for _, a := range f.Annotations {
if a.TypeName == "Docs" {
for _, e := range a.Elements {
switch e.QualifiedName {
case "projlab.Docs.uml":
e.Value = strings.ReplaceAll(e.Value, "\\\"", "🍆")
e.Value = strings.ReplaceAll(e.Value, "\"", "")
e.Value = strings.ReplaceAll(e.Value, "🍆", "\"")
uml = true
if e.Value != "" {
after += fmt.Sprintf("\n%s %s %s", f.Type.Name, e.Value, c.Name)
}
}
}
}
}
if !uml {
output.WriteString(fmt.Sprintf("%s%s: %s\n", f.GetVisibility(), f.Name, f.Type.Name))
}
}
for _, m := range c.Methods {
params := ""
for _, p := range m.Parameters {
params += fmt.Sprintf("%s: %s, ", p.Name, p.Type.Name)
}
params = strings.TrimSuffix(params, ", ")
mod := ""
if strings.Contains(m.Modifiers, "abstract") {
mod += " {abstract} "
}
if strings.Contains(m.Modifiers, "static") {
mod += " {static} "
}
output.WriteString(fmt.Sprintf("%s%s%s(%s): %s\n", m.GetVisibility(), mod, m.Name, params, m.ReturnType.Name))
}
output.WriteString(fmt.Sprintln("}"))
output.WriteString(fmt.Sprintln(after))
for _, a := range c.Annotations {
if a.TypeName == "Docs" {
for _, e := range a.Elements {
switch e.QualifiedName {
case "projlab.Docs.uml":
e.Value = strings.ReplaceAll(e.Value, "\\\"", "🍆")
e.Value = strings.ReplaceAll(e.Value, "\"", "")
e.Value = strings.ReplaceAll(e.Value, "🍆", "\"")
output.WriteString(fmt.Sprintln(e.Value))
}
}
}
}
output.WriteString(fmt.Sprintln())
}
almostDone := output.String()
lines := strings.Split(almostDone, "\n")
skip := make(map[string][]string)
for _, l := range lines {
if arrowRegex.MatchString(l) {
words := strings.Split(l, " ")
one := words[0]
other := words[len(words)-1]
skip[one] = append(skip[one], other)
skip[other] = append(skip[other], one)
}
}
for _, c := range classes {
out, e := json.Marshal(*c)
if e != nil {
panic(e)
}
str := string(out)
for _, c2 := range classes {
if c2.Name == c.Name {
continue
}
if strings.Contains(str, c2.Name) {
skips := skip[c2.Name]
shouldSkip := false
for _, s := range skips {
if c.Name == s {
shouldSkip = true
break
}
}
if !shouldSkip {
output.WriteString(fmt.Sprintf("%s ..> %s\n", c.Name, c2.Name))
}
}
}
}
output.WriteString(fmt.Sprintln("@enduml"))
e := ioutil.WriteFile(classFile, output.Bytes(), os.ModePerm)
if e != nil {
panic(e)
}
}
func fakePipeline() {
cmd := exec.Command("plantuml", "-tsvg", classFile)
b, e := cmd.CombinedOutput()
if e != nil {
fmt.Println(string(b))
panic(e)
}
outfile := strings.ReplaceAll(classFile, ".txt", ".eps")
cmd = exec.Command("inkscape", "-o", outfile, strings.ReplaceAll(classFile, ".txt", ".svg"))
b, e = cmd.CombinedOutput()
if e != nil {
fmt.Println(string(b))
panic(e)
}
fmt.Println(outfile)
}
......@@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"os/exec"
"os/user"
"path/filepath"
"sort"
"strings"
......@@ -20,6 +21,22 @@ type InternalType struct {
QualifiedName string
DocString string
Annotations []Annot
Modifiers string
}
func (i *InternalType) GetVisibility() string {
visibilities := map[string]string{
"public": "+",
"private": "-",
"protected": "#",
"package": "~",
}
for k, v := range visibilities {
if strings.Contains(i.Modifiers, k) {
return v
}
}
return visibilities["package"]
}
func (i *InternalType) GetAnnot() []Annot {
......@@ -44,12 +61,10 @@ type Method struct {
Parameters []*Params
ReturnType InternalType
Exceptions []struct{} // TODO
Modifiers string
}
type Field struct {
InternalType
Modifiers string
Type InternalType
}
......@@ -65,7 +80,7 @@ type Class struct {
realClass *Class
}
Methods []*Method
Modifiers string
Fields []*Field
}
......@@ -118,9 +133,14 @@ func genJavadoc(src string) error {
return e
}
doclet := "/root/jsonDoclet.jar"
if usr, err := user.Current(); err == nil {
doclet = usr.HomeDir + "/jsonDoclet.jar"
}
cmd := exec.Command("javadoc",
"-doclet", "com.raidandfade.JsonDoclet.Main",
"-docletpath", "/root/jsonDoclet.jar",
"-docletpath", doclet,
"-sourcepath", p,
"-private",
"-subpackages", "projlab")
......@@ -149,6 +169,24 @@ func readJson(fname string) (*Class, error) {
return nil, e
}
if c.Name == "Docs" {
return nil, nil
}
for _, a := range c.Annotations {
if a.TypeName == "Docs" {
for _, e := range a.Elements {
switch e.QualifiedName {
case "projlab.Docs.skip":
e.Value = strings.ReplaceAll(e.Value, "\"", "")
if e.Value == "true" {
return nil, nil
}
}
}
}
}
replaceFieldType := func(field interface{}, newType string) {
field.(*Field).Type.Name = newType
}
......@@ -167,6 +205,7 @@ func readJson(fname string) (*Class, error) {
switch e.QualifiedName {
case "projlab.Docs.type":
e.Value = strings.ReplaceAll(e.Value, "\"", "")
if e.Value != "" {
replace(arr[i], e.Value)
}
}
......@@ -174,16 +213,17 @@ func readJson(fname string) (*Class, error) {
}
}
}
}
tmp := make([]annotated, 0, len(c.Fields))
for i := range tmp {
tmp[i] = &c.Fields[i].InternalType
tmp := make([]annotated, len(c.Fields))
for i := range c.Fields {
tmp[i] = c.Fields[i]
}
do(tmp, replaceFieldType)
tmp = make([]annotated, 0, len(c.Methods))
for i := range tmp {
tmp[i] = &c.Methods[i].InternalType
tmp = make([]annotated, len(c.Methods))
for i := range c.Methods {
tmp[i] = c.Methods[i]
}
do(tmp, replaceMethodType)
......@@ -213,8 +253,10 @@ func readJavadoc() (map[string]*Class, error) {
if e != nil {
return nil, e
}
if c != nil {
m[c.QualifiedName] = c
}
}
return m, nil
}
......@@ -321,10 +363,6 @@ func printLatex(c map[string]*Class) {
}
func printClassDoc(c *Class) {
if c.QualifiedName == "projlab.Docs" {
return
}
fmt.Printf("\\subsubsection{\\texttt{%s \\textcolor{blue}{%s}}}\n", c.Modifiers, c.Name)
fmt.Println("\\begin{itemize}")
......
......@@ -3,7 +3,7 @@ package main
import "os"
func main() {
mainCmds := CmdFrom(os.Args[0], "go projlab tool", Timetable, Javadoc)
mainCmds := CmdFrom(os.Args[0], "go projlab tool", Timetable, Javadoc, Classdiag)
if len(os.Args) < 2 {
os.Args = append(os.Args, "help")
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment