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

com.gitee.l0km.javadocreader.JavadocReader Maven / Gradle / Ivy

There is a newer version: 1.1.0
Show newest version
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 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