com.gitee.l0km.javadocreader.JavadocReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javadocreader9 Show documentation
Show all versions of javadocreader9 Show documentation
read comments from java source using javadoc
package com.gitee.l0km.javadocreader;
import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.DocumentationTool;
import javax.tools.DocumentationTool.DocumentationTask;
import javax.tools.ToolProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitee.l0km.aocache.annotations.AoWeakCacheable;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import jdk.javadoc.doclet.DocletEnvironment;
import jdk.javadoc.doclet.Reporter;
/**
* 读取指定java源文件的javadoc信息
* @author guyadong
*
*/
public class JavadocReader {
protected static final Logger logger = LoggerFactory.getLogger(JavadocReader.class);
private static String sourcepath;
private static String[] sourcepathList = new String[0];
private static String classpath;
public static class Doclet implements jdk.javadoc.doclet.Doclet {
volatile static DocletEnvironment root = null;
@Override
public void init(Locale locale, Reporter reporter) {
logger.debug("Doclet using locale: " + locale);
}
@Override
public boolean run(DocletEnvironment docEnv) {
root = docEnv;
return true;
}
@Override
public String getName() {
return "Doclet";
}
@Override
public Set extends Option> getSupportedOptions() {
Option[] options = {
new Option() {
private final List someOption = List.of(
"-Xdoclint"
);
@Override
public int getArgumentCount() {
return 1;
}
@Override
public String getDescription() {
return "an option with aliases";
}
@Override
public Option.Kind getKind() {
return Option.Kind.STANDARD;
}
@Override
public List getNames() {
return someOption;
}
@Override
public String getParameters() {
return "file";
}
@Override
public boolean process(String opt, List arguments) {
return true;
}
}
};
return Set.of(options);
}
@Override
public SourceVersion getSupportedSourceVersion() {
// support the latest release
return SourceVersion.latest();
}
}
public static void show(){
if(null == Doclet.root){
return ;
}
Set classes = ElementFilter.typesIn(Doclet.root.getIncludedElements());
for (TypeElement typeElement:classes) {
new ExtClassDoc(Doclet.root,typeElement).output(System.out);
}
}
private static DocletEnvironment getRoot() {
if(null == Doclet.root) {
synchronized (JavadocReader.class) {
if(null == Doclet.root) {
readDocs(null, classpath, sourcepath);
}
}
}
return Doclet.root;
}
public static void resetRoot() {
Doclet.root = null;
}
/**
* 解析指定的java源文件返回 {@link DocletEnvironment}对象
* 参见 javadoc
* @param source a java source file or package name
* @param classpath value for '-classpath',{@code source}的class位置,可为{@code null},如果不提供,无法获取到完整的注释信息(比如annotation)
* @param sourcepath value for '-sourcepath'
* @return {@link DocletEnvironment}对象
*/
public synchronized static DocletEnvironment readDocs(String source, String classpath,String sourcepath) {
List args = Lists.newArrayList(
"-quiet","-Xmaxerrs","1","-Xmaxwarns","1","-encoding","utf-8","-private","-Xdoclint",
"none");
if(Strings.isNullOrEmpty(source) || !source.endsWith(".java")) {
/** source参数为包名或为空时,指定-subpackages参数,获取所有类注解 */
args.add("-subpackages");
args.add(subpackages(sourcepath));
}
if(!Strings.isNullOrEmpty(classpath)){
args.add("-classpath");
args.add(normailzePathSeparator(classpath));
}
if(!Strings.isNullOrEmpty(sourcepath)){
args.add("-sourcepath");
args.add(normailzePathSeparator(sourcepath));
}
if(!Strings.isNullOrEmpty(source)) {
args.add(source);
}
logger.debug("Javadoc Options:\n<<<<\n{}\n>>>>",Joiner.on("\n").join(args));
// 获取 DocumentationTool 实例
DocumentationTool docTool = ToolProvider.getSystemDocumentationTool();
DocumentationTask task = docTool.getTask(new PrintWriter(System.out), null, null, Doclet.class, args, null);
boolean sucessed = false;
try {
sucessed = task.call();
} catch (RuntimeException e) {
throw new IllegalStateException();
}
if(!sucessed) {
throw new IllegalStateException();
}
return Doclet.root;
}
/**
* 读取{@code source}指定源文件的注释
* @param source
* @param classpath
* @param sourcepath
* @return 读取失败返回{@code null}
*/
public synchronized static ExtClassDoc read(String source, String classpath,String sourcepath) {
try {
readDocs(source, classpath, sourcepath);
return new ExtClassDoc(Doclet.root,ElementFilter.typesIn(Doclet.root.getIncludedElements()).iterator().next());
} catch (IllegalStateException e) {
return null;
}catch (Exception e) {
e.printStackTrace();
return null;
}
}
/** @see #read(String, String) */
public static ExtClassDoc read(String source) {
return read(source,classpath);
}
public static ExtClassDoc read(String prefix,Class> clazz) {
return read(getSourceFile(prefix,clazz),classpath);
}
private static TypeElement classNamed(String classname) {
return ElementFilter.typesIn(getRoot().getIncludedElements()).stream()
.filter(e -> e.getQualifiedName().contentEquals(classname)).findFirst().orElse(null);
}
/**
* 根据类名查找注释对象
* @param qualifiedName 带包名的类全名
*/
@AoWeakCacheable
private static ExtClassDoc readQualifiedName(String qualifiedName) {
if (null != qualifiedName) {
try {
if (getRoot() == null) {
return null;
}
TypeElement classDoc = classNamed(qualifiedName);
if (null != classDoc) {
return new ExtClassDoc(getRoot(),classDoc);
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
return null;
}
@AoWeakCacheable
private static ExtClassDoc read0(Class> clazz) {
if (clazz == null) {
return null;
}
String qualifiedName = clazz.getName().replace('$', '.');
return readQualifiedName(qualifiedName);
}
public static ExtClassDoc read(Class> clazz) {
return read0(clazz);
}
public static ExtClassDoc read(String source, List classpath) {
return read(source,null == classpath?null:Joiner.on(File.pathSeparator).skipNulls().join(classpath));
}
public static ExtClassDoc read(String source, String[] classpath) {
return read(source,null == classpath?null:Joiner.on(File.pathSeparator).skipNulls().join(classpath));
}
public synchronized static ExtClassDoc read(String source, String classpath) {
return read(source,classpath,sourcepath);
}
public static ExtClassDoc read(String source, List classpath,List sourcepath) {
return read(source,
null == classpath ? null : Joiner.on(File.pathSeparator).skipNulls().join(classpath),
null == sourcepath ? null : Joiner.on(File.pathSeparator).skipNulls().join(sourcepath));
}
public static ExtClassDoc read(String source, String[] classpath,String[] sourcepath) {
return read(source,
null == classpath ? null : Joiner.on(File.pathSeparator).skipNulls().join(classpath),
null == sourcepath ? null : Joiner.on(File.pathSeparator).skipNulls().join(sourcepath));
}
/**
* 返回类的源文件位置
* @param prefix 源文件夹,可为{@code null}
* @param clazz 为{@code null}则返回{@code null}
* @return 返回'/'分隔'.java'结尾的路径名,for example, '/home/src/java/awt/Graphics*java‘
*/
public static String getSourceFile(String prefix,Class> clazz){
if(null != clazz ){
Class> innerClass = clazz;
Class> declaringClass = innerClass.getDeclaringClass();
while(declaringClass != null){
innerClass = declaringClass;
declaringClass = declaringClass.getDeclaringClass();
}
String source = innerClass.getName().replace('.', File.separatorChar) + ".java";
return prefix == null ? source : prefix + File.separator + source;
}
return null;
}
/**
* 在{@link #setSourcepath(String)}指定的源码路径中搜索类的源码文件
* @param clazz
* @return source file path or null if clazz is null or not found
*/
public static String findSourceFileInSourcepathList(Class> clazz){
if(null == clazz) {
return null;
}
for(String path:sourcepathList){
String source = getSourceFile(path, clazz);
if(new File(source).isFile()){
return source;
}
}
return null;
}
/**
* 返回是否存在指定类的源码,{@code clazz}为{@code null}返回{@code null}
* @param clazz
* @see #findSourceFileInSourcepathList(Class)
*/
public static boolean existSourceOf(Class> clazz) {
String file = findSourceFileInSourcepathList(clazz);
return null != file;
}
/**
* 返回类的源码位置路径
* @param clazz
* @return file path or null if clazz is null
* @see #getSourceFile(String, Class)
*/
public static String getSourceFile(Class> clazz){
if(null == clazz) {
return null;
}
String found = findSourceFileInSourcepathList(clazz);
if(null != found) {
return found;
}
return getSourceFile(null, clazz);
}
/**
* @return sourcepath
*/
public static String getSourcepath() {
return sourcepath;
}
/**
* @param sourcepath 要设置的 sourcepath
*/
public static void setSourcepath(String sourcepath) {
JavadocReader.sourcepath = sourcepath;
JavadocReader.sourcepathList = asPaths(sourcepath).toArray(new String[0]);
}
/**
* @return classpath
*/
public static String getClasspath() {
return classpath;
}
/**
* @param classpath 要设置的 classpath
*/
public static void setClasspath(String classpath) {
JavadocReader.classpath = classpath;
}
/**
* 归一化输入的路径列表中的路径分隔符
* 则将字符串中的';,:'替换为系统路径分割符({@link File#pathSeparator})
* @param input ';,:'分割的路径列表
* @return 返回归一化的字符串,输入为{@code null}则返回 {@code null}
*/
private static String normailzePathSeparator(String input) {
return Joiner.on(File.pathSeparator).join(asPaths(input));
}
/**
* 将{@code input}用{@code delim}指定的分隔符切分为不含空格和分隔符的一组字符串
* @param input 输入字符串
* @param delim 包含多个分隔符的字符串
* @return {@code input}或{@code delim}为{@code null}时返回空表
*/
private static final List elementsOf(String input,String delim) {
List list = new ArrayList();
if (input != null && delim != null) {
StringTokenizer st = new StringTokenizer(input, delim);
while (st.hasMoreTokens()) {
list.add(st.nextToken());
}
}
return list;
}
/**
* 将';,:'分割的字符串拆分为路径列表
* @param input ';,:'分割的路径列表
* @return 返回字符串列表,输入为{@code null}则返回空表
*/
private static List asPaths(String input) {
if(!Strings.isNullOrEmpty(input)){
List paths;
if (File.pathSeparatorChar == ':') {
paths = elementsOf(input,";,:");
}else {
paths = elementsOf(input,";,");
}
return FluentIterable.from(paths).transform(String::trim).toList();
}
return Collections.emptyList();
}
private static String subpackages(String sourcepath) {
FluentIterable subpackages = FluentIterable.of();
for (String p : asPaths(sourcepath)) {
File path = new File(p);
if (path.isDirectory()) {
subpackages = subpackages.append(path.list((dir, name) -> new File(dir, name).isDirectory()));
}
}
// 包名冒号分割
// 参见
// https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html#CHDJEDJI
return Joiner.on(":").join(subpackages.copyInto(new TreeSet<>()));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy