Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
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 extends Option> 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 extends Element> 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("\n" );
file.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(" ancestors \n" );
file.append(" ").append(value).append(" \n" );
file.append(" \n" );
}
value = wrap.descendants();
if(!Is.empty(value)) {
file.append(" \n" );
file.append(" descendants \n" );
file.append(" ").append(value).append(" \n" );
file.append(" \n" );
}
for(VariableWrap field : fields) {
file.append(" \n" );
file.append(" ").append(field.simpleName()).append(" \n" );
file.append(" ").append(field.javaTypeAsHtml(false)).append(" \n" );
file.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("
\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(" \n" );
file.append(" \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(" ")
.append(model.simpleName())
.append(" ")
.append(model.javadocFirst()).append(" \n" );
}
file.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(" \n" );
file.append(" \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(" ")
.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("
\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(" \n" );
file.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" );
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(" ").append(endpoint.method()).append(" \n" );
file.append(" ").append(endpoint.fullRootPath()).append(" \n" );
file.append(" \n" );
file.append("
\n" );
file.append(endpoint.javadoc());
file.append(" \n" );
file.append(endpoint.annotations());
file.append(" \n" );
file.append("
\n" );
file.append(" \n" );
file.append(" returns: \n" );
file.append(" ").append(endpoint.returnTypeAsHtml(false)).append(" \n" );
file.append(" \n" );
List params = endpoint.params();
if(!Is.empty(params)) {
for(VariableWrap param : params) {
file.append(" \n" );
file.append(" param ").append(param.simpleName()).append(" \n" );
file.append(" ").append(param.javaTypeAsHtml(false)).append(" \n" );
file.append(" ").append(param.annotations()).append(" \n" );
file.append(" \n" );
}
}
file.append(" \n" );
file.append(" throws \n" );
file.append(" ").append(endpoint.throwsAsHtml(false)).append(" \n" );
file.append(" \n" );
file.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 extends Element> 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 extends Element> 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 options = Set.of(
new Option("-author", 0, "an author", "") {}
, new Option("-bottom", 1, "a bottom", "") {}
, new Option("-charset", 1, "a charset", "") {}
, new Option("-d", 1, "a directory", "") {
@Override
public boolean process(String option, List arguments) {
destination = arguments.get(0);
return OK;
}
}
, new Option("-docencoding", 1, "a docencoding", "") {
@Override
public boolean process(String option, List arguments) {
docencoding = arguments.get(0);
return OK;
}
}
, new Option("-doctitle", 1, "a title", "") {
@Override
public boolean process(String option, List arguments) {
doctitle = arguments.get(0);
return OK;
}
}
, new Option("-linkoffline", 2, "a link", "") {}
, new Option("-use", 1, "an use", "") {}
, new Option("-version", 1, "a version", "") {}
, new Option("-windowtitle", 1, "a value", "") {}
, new Option("-docletpath", 1, "a value", "") {
@Override
public boolean process(String option, List arguments) {
// String path = arguments.get(0);
// try {
// System.out.println(" docletpath " + path);
// clresolver = new ClassResolver(path);
// System.out.println(" docletpath resolved " +clresolver);
// } catch(Exception e) {
// System.out.println(" error " + e);
// e.printStackTrace();
// }
return OK;
}
}
);
}