All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sk.antons.sb.rest.doclet.SBRestDoclet Maven / Gradle / Ivy

/*
 * Copyright 2020 Anton Straka
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package sk.antons.sb.rest.doclet;

import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.util.DocTreeScanner;
import com.sun.source.util.DocTrees;
import java.io.File;
import java.io.PrintStream;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementScanner9;
import jdk.javadoc.doclet.Doclet;
import jdk.javadoc.doclet.DocletEnvironment;
import jdk.javadoc.doclet.Reporter;
import java.util.logging.Logger;
import javax.tools.JavaFileManager;
import sk.antons.jaul.Get;
import sk.antons.jaul.Is;
import sk.antons.jaul.pojo.Messer;
import sk.antons.jaul.pojo.Pojo;
import sk.antons.jaul.util.TextFile;
import sk.antons.sb.rest.doclet.cl.Cl;
import sk.antons.sb.rest.doclet.cl.ClDb;
import sk.antons.sb.rest.doclet.finder.ControllerFinder;
import sk.antons.sb.rest.doclet.finder.EndpointFinder;
import sk.antons.sb.rest.doclet.json.Jsonizer;
import sk.antons.sb.rest.doclet.resource.ResourceLoader;
import sk.antons.sb.rest.doclet.wrap.ControllerWrap;
import sk.antons.sb.rest.doclet.wrap.EndpointWrap;
import sk.antons.sb.rest.doclet.wrap.ModelWrap;
import sk.antons.sb.rest.doclet.wrap.VariableWrap;
import sk.antons.sb.rest.doclet.wrap.WrapEnv;
/**
 *
 * @author antons
 */
public class SBRestDoclet implements Doclet {
    private static Logger log = Logger.getLogger(SBRestDoclet.class.getName());
    
    
    @Override
    public void init(Locale locale, Reporter reporter) { 
        this.reporter = reporter;
    }
    
    
    @Override
    public String getName() {
        return getClass().getSimpleName();
    }
 
    @Override
    public Set getSupportedOptions() {
        return options;
        //return Collections.emptySet();
    }
 
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latest();
    }

 
    private static final boolean OK = true;
    private static final boolean FAILED= false;
 
    private boolean showElements = true;
    private boolean showComments = true;
 
    private Reporter reporter;
    private DocTrees treeUtils;
    private JavaFileManager fileManager;
    private ClassResolver clresolver = new ClassResolver("");
    private String destination;
    private String doctitle;
    private String docencoding;
    WrapEnv env;
    ClDb classDb = new ClDb();
    Messer messer = Pojo.messer();
    Jsonizer jsonizer = Jsonizer.instance();
 
    @Override
    public boolean run(DocletEnvironment environment) {
        try {
            
            note("init");
            //System.out.println(System.getProperties());
            //System.out.println(" ------- BasicDoclet start -----------");
            
            //System.out.println(" init1 ");
            treeUtils = environment.getDocTrees();
            //System.out.println(" init2 ");
            fileManager = environment.getJavaFileManager();
            //System.out.println(" init3 ");
            //clresolver.init(fileManager);
            //System.out.println(" init4 ");
            if(Is.empty(docencoding)) docencoding = "UTF-8";
            if(Is.empty(doctitle)) doctitle = "REST API";
            if(Is.empty(destination)) destination = "./target/site/apidocs";
            env = new WrapEnv();
            env.setClassDb(classDb);
            env.setTreeUtils(treeUtils);
            
    //        for(Element includedElement : environment.getIncludedElements()) {
    //            System.out.println(" included: " + includedElement);
    //        }
    //        for(Element includedElement : environment.getSpecifiedElements()) {
    //            System.out.println(" specified: " + includedElement);
    //        }
            createFolders();
            
            //System.out.println(" build class list ");
            //traverseElements(environment.getIncludedElements(), "", classDb, included);
            buildClassDb(environment.getIncludedElements(), classDb);
            //classDb.print();
            //System.out.println(" build class tree start ");
            classDb.buildTree();
            //System.out.println(" build class tree end ");
            classDb.addMesserMapping(messer, clresolver);
            Set specifiedElements = environment.getSpecifiedElements();
            //traverseElements(specifiedElements, "");

            
            ControllerFinder controllerfinder = new ControllerFinder();
            List controllers = controllerfinder.findIn(specifiedElements);
            note("number of controllers " + Get.size(controllers));
            //System.out.println(" controllers: " + controllers);
            EndpointFinder endpointfinder = new EndpointFinder();
            List endpointsElems = endpointfinder.findIn(controllers);
            List endpoints  = EndpointWrap.toEndpoints(endpointsElems, env);
            Set modelClasses = new HashSet<>();
            for(EndpointWrap endpoint : endpoints) {
                endpoint.closure();
            }
            note("number of endpoints " + Get.size(endpoints));
    //        for(String string : env.used()) {
    //            System.out.println(" used --- " +string);
    //        }
            classDb.closure(env);
            note("number of model classes " + Get.size(env.used()));
            note("process model classes...");
            for(String string : env.used()) {
                processModel(string);
    //            System.out.println(" used --- " +string);
    //            Cl cl = classDb.get(string);
    //            if(cl == null) {
    //                System.out.println("    no cl");
    //            } else {
    //                System.out.println("     included --- " + environment.isIncluded(cl.element()));
    //                System.out.println("     selected --- " + environment.isSelected(cl.element()));
    //                String javadoc = ControllerWrap.instance(cl.element(), env).javadoc();
    //                if(Is.empty(javadoc)) System.out.println("    no javadoc");
    //            }
            }

            note("process endpoint index...");
            processEndpoints(endpointsElems);
            note("process model index...");
            processModels(env.used());
            note("process controllers...");
            for(Element element : controllers) {
                processController(element);
            }
            note("process done...");
        } catch(Exception e) {
            note("process failed..." + e);
        }
        return OK;
    }

    private void createFolders() {
        File f = new File(destination + "/css");
        if(!f.exists()) f.mkdirs();
        f = new File(destination + "/rest");
        if(!f.exists()) f.mkdirs();
        f = new File(destination + "/model");
        if(!f.exists()) f.mkdirs();
        //System.out.println(" ---- main.css");
        String css = ResourceLoader.resource("css/main.css");
        //System.out.println(" ---- main.css " + css);
        TextFile.save(destination + "/css/main.css", docencoding, css);
    }
    
    private void processModel(String fqn) {
        Cl cl = classDb.get(fqn);
        if(cl == null) {
            //System.out.println(" no class fqn");
            return;
        }
        TypeElement te = (TypeElement)cl.element();
        ModelWrap wrap = ModelWrap.instance(te, env);
        StringBuilder file = new StringBuilder();
        file.append(fileprefix("../css/main.css"));
        file.append(" 
\n"); file.append("
\n"); file.append("\n" ); file.append("
\n" ); file.append(" rest\n" ); file.append(" model\n" ); file.append("
\n" ); file.append("\n" ); file.append("
").append(wrap.simpleName()).append("
\n" ); file.append("\n" ); file.append("
\n" ); file.append("\n" ); file.append(wrap.javadoc()); file.append("\n" ); file.append(wrap.annotations()); file.append("\n" ); file.append("
\n" ); file.append("\n" ); List fields = wrap.fields(); file.append("\n" ); if(!Is.empty(fields)) { file.append("
\n" ); file.append(" \n" ); String value = wrap.ancestors(); if(!Is.empty(value)) { file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); } value = wrap.descendants(); if(!Is.empty(value)) { file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); } for(VariableWrap field : fields) { file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); } file.append("
ancestors").append(value).append("
descendants").append(value).append("
").append(field.simpleName()).append("").append(field.javaTypeAsHtml(false)).append("\n" ); value = field.javadoc(); if(!Is.empty(value)) { file.append(value).append("\n" ); } value = field.annotations(); if(!Is.empty(value)) { file.append(value).append("\n" ); } file.append("
\n" ); file.append("
\n" ); file.append("
\n" ); file.append(json(fqn)); file.append("
\n" ); file.append("
\n" ); file.append("
\n" ); } file.append("
\n" ); file.append(ResourceLoader.resource("html/postfix.html")); TextFile.save(destination + "/model/"+te.getQualifiedName()+".html", docencoding, file.toString()); } private void processModels(List used) { Collections.sort(used); StringBuilder file = new StringBuilder(); file.append(fileprefix("./css/main.css")); file.append("
\n"); file.append("
\n"); file.append(" \n" ); file.append("
\n" ); file.append(" rest\n" ); file.append(" model\n" ); file.append("
\n" ); file.append(" \n" ); file.append("
model list
\n" ); file.append(" \n" ); file.append("
\n" ); file.append(" \n" ); file.append("\n" ); if(!Is.empty(used)) { file.append(" \n" ); for(String use : used) { ModelWrap model = ModelWrap.instance(classDb.get(use).element(), env); file.append(" \n" ); } file.append("
") .append(model.simpleName()) .append("") .append(model.javadocFirst()).append("
\n" ); file.append(" \n" ); file.append("
\n" ); file.append("
\n" ); file.append("
\n" ); } file.append(ResourceLoader.resource("html/postfix.html")); TextFile.save(destination + "/index-model.html", docencoding, file.toString()); } private void processEndpoints(List elements) { StringBuilder file = new StringBuilder(); file.append(fileprefix("./css/main.css")); file.append("
\n"); file.append("
\n"); file.append(" \n" ); file.append("
\n" ); file.append(" rest\n" ); file.append(" model\n" ); file.append("
\n" ); file.append(" \n" ); file.append("
rest list
\n" ); file.append(" \n" ); file.append("
\n" ); file.append(" \n" ); file.append("\n" ); if(!Is.empty(elements)) { file.append(" \n" ); List endpoints = EndpointWrap.toEndpoints(elements, env); Collections.sort(endpoints); for(EndpointWrap endpoint : endpoints) { file.append(" \n" ); } file.append("
") .append(endpoint.method()) .append(" ").append(endpoint.fullRootPath()).append(" ") .append(endpoint.javadocFirst()).append("
\n" ); file.append(" \n" ); file.append("
\n" ); file.append("
\n" ); file.append("
\n" ); } file.append(ResourceLoader.resource("html/postfix.html")); TextFile.save(destination + "/index-rest.html", docencoding, file.toString()); } private void processController(Element element) { TypeElement te = (TypeElement)element; ControllerWrap wrap = ControllerWrap.instance(element, env); StringBuilder file = new StringBuilder(); file.append(fileprefix("../css/main.css")); file.append("
\n"); file.append("
\n"); file.append(" \n" ); file.append("
\n" ); file.append(" rest\n" ); file.append(" model\n" ); file.append("
\n" ); file.append(" \n" ); file.append("
").append(wrap.simpleName()).append("
\n" ); file.append(" \n" ); file.append("
\n" ); file.append(" \n" ); file.append("
\n" ); file.append(" root path: ").append(wrap.rootPath()).append("\n" ); file.append("
\n" ); file.append(wrap.javadoc()); file.append("\n" ); file.append(wrap.annotations()); file.append("\n" ); file.append("
\n" ); file.append("\n" ); List endpoints = wrap.endpoints(); Collections.sort(endpoints); if(!Is.empty(endpoints)) { file.append(" \n" ); for(EndpointWrap endpoint : endpoints) { file.append(" \n" ); } file.append("
") .append(endpoint.method()) .append(" ").append(endpoint.fullRootPath()).append(" ") .append(endpoint.javadocFirst()).append("
\n" ); file.append(" \n" ); file.append("
\n" ); } file.append("\n" ); if(!Is.empty(endpoints)) { for(EndpointWrap endpoint : endpoints) { file.append("
\n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append("
").append(endpoint.method()).append("").append(endpoint.fullRootPath()).append("
\n" ); file.append(endpoint.javadoc()); file.append(" \n" ); file.append(endpoint.annotations()); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); List params = endpoint.params(); if(!Is.empty(params)) { for(VariableWrap param : params) { file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); } } file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append(" \n" ); file.append("
returns:").append(endpoint.returnTypeAsHtml(false)).append("
param ").append(param.simpleName()).append("").append(param.javaTypeAsHtml(false)).append("").append(param.annotations()).append("
throws").append(endpoint.throwsAsHtml(false)).append("
\n" ); file.append("
\n" ); } } file.append("
\n" ); file.append("
\n" ); file.append("
\n" ); // System.out.println(" ---------- " + element.getSimpleName() + " -----------"); // System.out.println(" " + te.getQualifiedName()); // AnnotationMirror requestMapping = ElementHelper.annotatoonByClass(element, "org.springframework.web.bind.annotation.RequestMapping"); // String root = ElementHelper.annotationParam(requestMapping, "path"); // System.out.println(" - root: " + root); // TreePath dct = treeUtils.getPath(element); // System.out.println(" - tp: " + dct); // //DocCommentTree dcTree = treeUtils.getDocCommentTree(dct); // //DocCommentTree dcTree = treeUtils.getDocCommentTree(dct.getCompilationUnit().getSourceFile()); // DocCommentTree dcTree = treeUtils.getDocCommentTree(element); // if(dcTree != null) { // System.out.println(" - first: " + dcTree.getFirstSentence()); // System.out.println(" - fullbody: " + dcTree.getFullBody()); // System.out.println(" - getBody: " + dcTree.getBody()); // System.out.println(" - getPreamble: " + dcTree.getPreamble()); // System.out.println(" - getPostamble: " + dcTree.getPostamble()); // System.out.println(" - getBlockTags: " + dcTree.getBlockTags()); // System.out.println(" - all: " + dcTree); // System.out.println(" - getDoc: " + treeUtils.getDocComment(dct)); // } file.append(ResourceLoader.resource("html/postfix.html")); TextFile.save(destination + "/rest/"+te.getQualifiedName()+".html", docencoding, file.toString()); } private String fileprefix(String css) { String s = ResourceLoader.resource("html/prefix.html"); s = s.replace("CSS_URL", css); return s; } private void processEndpoint(Element element) { //System.out.println(" -m--------- " + element.getSimpleName() + " -----------"); ExecutableElement ee = (ExecutableElement)element; //System.out.println(" - return: " + ee.getReturnType()); //System.out.println(" - params: " + ee.getParameters()); } private void traverseElements(Collection collection, String prefix, ClDb classDb, Set included) { if(collection == null) return; for(Element element : collection) { traverseElement(element, prefix, classDb, included); } } private void traverseElement(Element e, String prefix, ClDb classDb, Set included) { if(e == null) return; if( (e.getKind() == ElementKind.CLASS) || (e.getKind() == ElementKind.INTERFACE) || (e.getKind() == ElementKind.ENUM) ) { String text = e.toString(); if(included.contains(text)) classDb.add(e); } traverseElements(e.getEnclosedElements(), prefix + " ", classDb, included); // System.out.println(prefix + " " + e.getKind() + " - " + e.getSimpleName()); // DocCommentTree dcTree = treeUtils.getDocCommentTree(e); // System.out.println(prefix + " dcTree: " + dcTree); // if(e.getKind() == ElementKind.CLASS) { // TypeElement te = (TypeElement)e; // String clname = te.getQualifiedName().toString(); // try { // //Class dl = Class.forName(clname, true, SBRestDoclet.class.getClassLoader().getParent().getParent()); // //Class dl = SBRestDoclet.class.getClassLoader().loadClass(clname); // //Class dl = Thread.currentThread().getContextClassLoader().loadClass(clname); // //Class dl = clresolver.resolve(clname); // Class dl = Class.forName(clname); // System.out.println(" class "+clname+" existexist"); // System.out.println(" - json: " + Pojo.dumper().jsonPretty(Pojo.messer().junk(dl), " ")); // } catch(Throwable ex) { // System.out.println(" class "+clname+" notexist " + ex); // } // // } } private String json(String fqn) { if(Is.empty(fqn)) return ""; try { Class dl = clresolver.resolve(fqn); Object o = Pojo.messer().junk(dl); //String json = Pojo.dumper().jsonPretty(o, " "); String json = jsonizer.jsonize(o); return json; } catch(Throwable ex) { //System.out.println(" class "+fqn+" notexist " + ex); return ""; } } private void note(String note) { System.out.println("sbrd: " + note); } private void buildClassDb(Set list, ClDb classDb) { if(Is.empty(list)) return; for(Element e: list) { if( (e.getKind() == ElementKind.CLASS) || (e.getKind() == ElementKind.INTERFACE) || (e.getKind() == ElementKind.ENUM) ) { classDb.add(e); } } } abstract class Option implements Doclet.Option { private final String name; private final int argCount; private final String description; private final String parameters; Option(String name, int argCount, String description, String parameters) { this.name = name; this.argCount = argCount; this.description = description; this.parameters = parameters; } @Override public int getArgumentCount() { return argCount; } @Override public String getDescription() { return description; } @Override public Kind getKind() { return Kind.STANDARD; } @Override public List getNames() { return List.of(name); } @Override public String getParameters() { return argCount > 0 ? parameters : null; } @Override public boolean process(String option, List arguments) { //System.out.println(" option '" + option + "' - "+ arguments); return OK; } } private final Set




© 2015 - 2025 Weber Informatics LLC | Privacy Policy