Select Git revision
Tóth Miklós Tibor authored
javadoc.go 8.08 KiB
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
)
type annotated interface {
GetAnnot() []Annot
}
type InternalType struct {
Name string
QualifiedName string
DocString string
Annotations []Annot
}
func (i *InternalType) GetAnnot() []Annot {
return i.Annotations
}
type Annot struct {
TypeName string
Elements []struct {
InternalType
Value string
}
}
type Params struct {
InternalType
Type InternalType
}
type Method struct {
InternalType
Parameters []*Params
ReturnType InternalType
Exceptions []struct{} // TODO
Modifiers string
}
type Field struct {
InternalType
Modifiers string
Type InternalType
}
type Class struct {
InternalType
Constructors []Method
Interfaces []struct {
InternalType
realClass *Class
}
Superclass struct {
InternalType
realClass *Class
}
Methods []*Method
Modifiers string
Fields []*Field
}
func (c *Class) getMethod(name string) *Method {
for i, m := range c.Methods {
if m.Name == name {
return c.Methods[i]
}
}
return nil
}
var Javadoc = Subcommand{
Name: "javadoc",
Command: func(args []string) {
if len(args) > 1 {
fmt.Println("usage: javadoc [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)
printLatex(classes)
},
Help: "create and parse javadoc from code",
}
func genJavadoc(src string) error {
const path = "/tmp/javadoc"
e := os.MkdirAll(path, os.ModePerm)
if e != nil {
return e
}
p, e := filepath.Abs(src)
if e != nil {
return e
}
cmd := exec.Command("javadoc",
"-doclet", "com.raidandfade.JsonDoclet.Main",
"-docletpath", "/root/jsonDoclet.jar",
"-sourcepath", p,
"-private",
"-subpackages", "projlab")
cmd.Dir = path
b, e := cmd.CombinedOutput()
if e != nil {
fmt.Println(string(b))
}
return e
}
func readJson(fname string) (*Class, error) {
var c Class
b, e := ioutil.ReadFile(fname)
if e != nil {
return nil, e
}
e = json.Unmarshal(b, &c)
if e != nil {
return nil, e
}
replaceFieldType := func(field interface{}, newType string) {
field.(*Field).Type.Name = newType
}
replaceMethodType := func(field interface{}, newType string) {
field.(*Method).ReturnType.Name = newType
}
replaceMethodParamType := func(field interface{}, newType string) {
field.(*Params).Type.Name = newType
}
do := func(arr []annotated, replace func(field interface{}, newType string)) {
for i, f := range arr {
ann := f.GetAnnot()
for _, a := range ann {
if a.TypeName == "Docs" {
for _, e := range a.Elements {
switch e.QualifiedName {
case "projlab.Docs.type":
e.Value = strings.ReplaceAll(e.Value, "\"", "")
replace(arr[i], e.Value)
}
}
}
}
}
}
tmp := make([]annotated, 0, len(c.Fields))
for i := range tmp {
tmp[i] = &c.Fields[i].InternalType
}
do(tmp, replaceFieldType)
tmp = make([]annotated, 0, len(c.Methods))
for i := range tmp {
tmp[i] = &c.Methods[i].InternalType
}
do(tmp, replaceMethodType)
tmp = make([]annotated, 0)
for i := range c.Methods {
for j := range c.Methods[i].Parameters {
tmp = append(tmp, c.Methods[i].Parameters[j])
}
}
do(tmp, replaceMethodParamType)
return &c, e
}
func readJavadoc() (map[string]*Class, error) {
const dirname = "/tmp/javadoc/docs"
files, err := ioutil.ReadDir(dirname)
if err != nil {
return nil, err
}
m := make(map[string]*Class)
for _, i := range files {
c, e := readJson(dirname + "/" + i.Name())
if e != nil {
return nil, e
}
m[c.QualifiedName] = c
}
return m, nil
}
func linkInheritance(c map[string]*Class) map[string]*Class {
for _, v := range c {
for k, i := range v.Interfaces {
interf, ok := c[i.QualifiedName]
if ok {
v.Interfaces[k].realClass = interf
}
}
supclass, ok := c[v.Superclass.QualifiedName]
if ok {
v.Superclass.realClass = supclass
}
}
return c
}
func resolveDocsFromInheritance(c map[string]*Class) map[string]*Class {
for _, cl := range c {
// magic incoming
finder := func(where []*InternalType, docReplacer func(c *Class) []*InternalType) {
for _, m := range where {
if m.DocString == "" {
for _, repl := range docReplacer(cl) {
if repl != nil && repl.Name == m.Name && repl.DocString != "" {
m.DocString = repl.DocString
break
}
}
}
}
}
getSupClasses := func(c *Class) []*Class {
tmp := make([]*Class, 0)
if c.Superclass.realClass != nil {
tmp = append(tmp, c.Superclass.realClass)
}
for _, in := range c.Interfaces {
if in.realClass != nil {
tmp = append(tmp, in.realClass)
}
}
return tmp
}
// methods
{
tmp := make([]*InternalType, len(cl.Methods))
for i, m := range cl.Methods {
tmp[i] = &m.InternalType
}
finder(tmp, func(c *Class) []*InternalType {
tmp := make([]*InternalType, 0)
sc := getSupClasses(c)
for _, s := range sc {
for _, m := range s.Methods {
tmp = append(tmp, &m.InternalType)
}
}
return tmp
})
}
// fields
{
tmp := make([]*InternalType, len(cl.Fields))
for i, m := range cl.Fields {
tmp[i] = &m.InternalType
}
finder(tmp, func(c *Class) []*InternalType {
tmp := make([]*InternalType, 0)
sc := getSupClasses(c)
for _, s := range sc {
for _, m := range s.Fields {
tmp = append(tmp, &m.InternalType)
}
}
return tmp
})
}
}
return c
}
func printLatex(c map[string]*Class) {
keys := make([]string, 0, len(c))
for k := range c {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool {
return keys[i] < keys[j]
})
for _, k := range keys {
class := c[k]
printClassDoc(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}")
if c.DocString != "" {
fmt.Println("\\item Felelősség:")
fmt.Println("\\newline")
fmt.Println(c.DocString)
}
fmt.Println("\\item Ősosztályok:")
fmt.Println("\\newline")
if c.Superclass.realClass != nil {
var loopy func(c *Class)
loopy = func(c *Class) {
if c.Superclass.realClass != nil {
loopy(c.Superclass.realClass)
fmt.Print(" $\\rightarrow$ ")
}
fmt.Printf("\\texttt{\\textcolor{blue}{%s}}", c.Name)
}
loopy(c)
fmt.Println()
} else {
fmt.Println("\\emph{Nincs ősosztály}")
}
if !strings.Contains(c.Modifiers, "interface") {
fmt.Println("\\item Interfészek:")
if len(c.Interfaces) == 0 {
fmt.Println("\\newline")
fmt.Println("\\emph{Nem valósít meg interfészeket}")
} else {
fmt.Println("\\begin{itemize}")
for _, i := range c.Interfaces {
fmt.Printf("\\item %s\n", i.Name)
}
fmt.Println("\\end{itemize}")
}
fmt.Println("\\item Attribútumok")
if len(c.Fields) == 0 {
fmt.Println("\\newline")
fmt.Println("\\emph{Nincsenek attribútumok}")
} else {
fmt.Println("\\begin{itemize}")
for _, attr := range c.Fields {
fmt.Printf(`\item \texttt{%s \textcolor{blue}{%s} %s}`, attr.Modifiers, attr.Type.Name, attr.Name)
if attr.DocString != "" {
fmt.Printf(": %s", attr.DocString)
}
fmt.Println()
}
fmt.Println("\\end{itemize}")
}
}
fmt.Println("\\item Metódusok")
if len(c.Methods) == 0 {
fmt.Println("\\newline")
fmt.Println("\\emph{Nincsenek metódusok}")
} else {
fmt.Println("\\begin{itemize}")
for _, meth := range c.Methods {
fmt.Printf(`\item \texttt{%s \textcolor{blue}{`, meth.Modifiers)
fmt.Printf("%s} ", meth.ReturnType.Name)
fmt.Printf("%s(", meth.Name)
for i, par := range meth.Parameters {
fmt.Printf("\\textcolor{blue}{%s} %s", par.Type.Name, par.Name)
if i != len(meth.Parameters)-1 {
fmt.Print(", ")
}
}
fmt.Print(")")
fmt.Printf("}")
if meth.DocString != "" {
fmt.Printf(": %s", meth.DocString)
}
fmt.Println()
}
fmt.Println("\n\\end{itemize}")
}
fmt.Println("\\end{itemize}")
fmt.Println()
fmt.Println()
}