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

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

There is a newer version: 1.1.0
Show newest version
package com.gitee.l0km.javadocreader;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

import com.google.common.collect.Lists;
import com.gitee.l0km.aocache.annotations.AoWeakCacheable;
import com.gitee.l0km.javadocreader.internal.TypeNames;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Maps;

import javax.lang.model.element.TypeElement;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;

import com.sun.source.util.DocTrees;
import com.sun.source.doctree.*;

import jdk.javadoc.doclet.DocletEnvironment;

import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;;

public class ExtClassDoc {
	public static final String NEW_LINE = System.getProperty("line.separator");
	public static enum Type{
		CLASS,METHOD,FIELD,NONE(0),ALL((~0));
		static {
			// 修改 ALL 的mask 值
			int mask = 0;
			for(Type v :Type.values()){
				if(1 == Integer.bitCount(v.mask))mask |=v.mask;
			}
			try {
				Field field = Type.class.getDeclaredField("mask");
				field.setAccessible(true);
				field.set(ALL, mask);
			} catch (Exception e) {
				throw new ExceptionInInitializerError(e);
			}
			// 检查所有组合mask值的合法性,超出范围则抛出异常
			for(Type v :Type.values()){
				if( Integer.bitCount(v.mask) > 1 && v != ALL){
					if(((~ALL.mask)&v.mask) != 0)
						throw new ExceptionInInitializerError(String.format("%s: %s,invalid mask, out of ALL scope",v.name(), 
								Integer.toBinaryString(v.mask)));
				}
			}
		}
		private final int mask;
		Type(){
			this.mask=(1<values){
			// 过滤掉null元素
			return null == values ? 0 :sum(Collections2.filter(values, new Predicate (){
				@Override
				public boolean apply(Type input) {
					return null !=input;
				}}).toArray(new Type[0]));
		}
		public static int sumOfString(Collectionvalues){
			return null == values ? 0 :sum(Collections2.transform(values, new Function(){
				@Override
				public Type apply(String input) {
					try{
						return Type.valueOf(input);
					}catch(Exception e){
						return null;
					}
				}}));
		}
		public static ListcheckAll(int value){
			ArrayList list = new ArrayList();
			for(Type v:Type.values()){
				if(v.check(value) && Integer.bitCount(v.mask) == 1)
					list.add(v);
			}	
			return list;
		}
	}
	public static enum Action{
		ADD,OVERWRITE,APPEND
	}
	public static enum AddColumn{
		ACTION,SCOPE
	}
	final DocletEnvironment docEnv;
	final TypeElement classDoc;
	final Elements elementUtils;
	final Types typeUtils;
	final DocTrees docTrees;
	/** 缩进字符串 */
	private static String indent = "    ";
	/** 输出comment时要排除的{@link DocTree}名字,比如{@code '@throws'} */
	private final Map excludeTags=Collections.synchronizedMap(new HashMap());
	/** 输出comment时要添加在commentText中的内容 */
	private final HashBasedTable additionalTextTable = HashBasedTable.create();
	private final CommentTextRender render;
	public ExtClassDoc(DocletEnvironment docEnv,TypeElement classDoc) {
		super();
		this.classDoc = checkNotNull(classDoc, "classDoc is null");
		this.docEnv = checkNotNull(docEnv, "docEnv is null");
		this.elementUtils=docEnv.getElementUtils();
		this.typeUtils=docEnv.getTypeUtils();
		this.docTrees = docEnv.getDocTrees();
    	this.render = new CommentTextRender();
	}

	/**
	 * 如果两个类型字符串匹配,返回{@code true},否则返回{@code false}
	 * @param docType
	 * @param type
	 */
	@AoWeakCacheable
	private boolean equalType(TypeMirror docType,java.lang.reflect.Type type) {
		String typeName = TypeNames.getTypeName(type).replaceAll("\\s+", "");
		return typeName.equals(docType.toString().replaceAll("\\s+", ""));
	}
	/**
	 * 如果两个类型字符串匹配,返回{@code true},否则返回{@code false}
	 * @param typeElement
	 * @param clazz
	 */
	@AoWeakCacheable
	private boolean equalType(TypeElement typeElement,Class clazz) {
		String typeName = TypeNames.getTypeName(clazz);
		return typeName.equals(typeElement.getQualifiedName().toString());
	}
	/**
	 * 检查两个方法对象的签名是否匹配
* @param member * @param doc * @return 不匹配返回 {@code false} ,匹配返回 {@code true} */ private boolean match(Member member, Element doc) { if (!member.getName().equals(doc.getSimpleName().toString())){ return false; } if(member instanceof Field) { return true; } checkArgument(member instanceof Executable,"INVALID member type %s,Method or Constructor required",member.getClass().getSimpleName()); java.lang.reflect.Type[] paramTypes = ((Executable)member).getGenericParameterTypes(); checkArgument(doc instanceof ExecutableElement,"INVALID doc type %s,ExecutableElement required",doc.getClass().getSimpleName()); List parameters = ((ExecutableElement)doc).getParameters(); if (paramTypes.length != parameters.size()) { return false; } for (int i = 0; i < paramTypes.length; ++i) { if(!equalType(parameters.get(i).asType(),paramTypes[i])) { return false; } } return true; } /** * 在{@link TypeElement}中查找与 {@link Method} 匹配的{@link ExecutableElement}
* 如果没有在当前方法上找到注释且是重写方法,则尝试向上父类查找父类方法 * @param method * @return 没有找则返回{@code null} * @see #getMemberDoc(TypeElement, Member) */ public ExecutableElement getMethodDoc(Method method) { ExecutableElement doc = (ExecutableElement) getMemberDoc(classDoc,method); while(null != doc && Strings.isNullOrEmpty(elementUtils.getDocComment(doc))){ // 如果没有注释,向上父类查找被重写的方法 doc = findMethodsInSuperClass(doc); } return doc; } private ExecutableElement findMethodsInSuperClass(ExecutableElement methodDoc) { TypeMirror typeMirror = ((TypeElement) methodDoc.getEnclosingElement()).getSuperclass(); return findMethodsInSuperClass(typeMirror,methodDoc); } private ExecutableElement findMethodsInSuperClass(TypeMirror superclass, ExecutableElement method) { if (null == superclass || superclass.getKind().isPrimitive() || superclass.toString().equals("java.lang.Object")) { return null; // 到达根类 } TypeElement superClassElement = (TypeElement) typeUtils.asElement(superclass); List enclosedElements = superClassElement.getEnclosedElements(); for (Element element : enclosedElements) { if (element instanceof ExecutableElement) { ExecutableElement superMethod = (ExecutableElement) element; // 检查该方法是否重写 if (elementUtils.overrides(method,superMethod, (TypeElement) method.getEnclosingElement())) { return superMethod; } } } // 递归查找超类 return findMethodsInSuperClass(superClassElement.getSuperclass(), method); } /** * * [递归]在{@link TypeElement}中递归查找与method匹配的{@link Element}对象
* @see #findMember(TypeElement, Member) */ private Element getMemberDoc(TypeElement classDoc,Member member) { if (null == classDoc || null == member){ return null; } Element matched = findMember(classDoc, member); if(matched == null){ return getMemberDoc((TypeElement) typeUtils.asElement(classDoc.getSuperclass()), member); } return matched; } /** * 在{@link TypeElement}中查找与 {@link Executable} 匹配的{@link ExecutableElement}
* @param executable executable * @return 没有找则返回{@code null} * @see #getMemberDoc(TypeElement, Member) */ public ExecutableElement getExecutableMemberDoc(Executable executable) { return (ExecutableElement) getMemberDoc(classDoc,executable); } /** * 在{@link TypeElement}中查找与 {@link Member} 匹配的{@link Element}
* @param member member * @return 没有找则返回{@code null} * @see #getMemberDoc(TypeElement, Member) */ public Element getMemberDoc(Member member) { return getMemberDoc(classDoc,member); } /** * 在{@link TypeElement}中查找指定方法或构造方法的参数名 * @param member Method or Constructor * @return 参数名列表,找不到返回null */ public String[] getParamerNames(Member member) { ExecutableElement memberDoc = getExecutableMemberDoc((Executable)member); if(memberDoc == null){ return null; } List parameters = memberDoc.getParameters(); String[] names = new String[parameters.size()]; for(int i = 0; i < names.length; ++i){ names[i] = parameters.get(i).getSimpleName().toString(); } return names; } /** * 在{@link TypeElement}中递归查找与name匹配的{@link VariableElement}
* @param classDoc * @param name field name * @return 没有找则返回{@code null} */ private VariableElement getFieldDoc(TypeElement classDoc, String name) { if (null == classDoc || null == name) { return null; } Optional opt = ElementFilter.fieldsIn(classDoc.getEnclosedElements()).stream() .filter(e -> e.getSimpleName().contentEquals(name)).findFirst(); if (opt.isPresent()) { return opt.get(); } TypeMirror superClass = classDoc.getSuperclass(); return getFieldDoc(null == superClass ? null : (TypeElement) typeUtils.asElement(superClass), name); } /** * 在{@link TypeElement}中查找与name匹配的{@link VariableElement}
* @param name field name * @return 没有找则返回{@code null} */ public VariableElement getFieldDoc(String name) { return getFieldDoc(classDoc,name); } ParamTree paramTagOf(ExecutableElement methodDoc,String name){ if(null == methodDoc) { return null; } if(!Strings.isNullOrEmpty(name)){ DocCommentTree docCommentTree = docTrees.getDocCommentTree(methodDoc); return new BlockTagExtracter().extract(docCommentTree).paramTag(name); } return null; } /** * 类注释如果有 '@deprecated' 注解则返回{@code true},否则返回{@code false} */ public boolean isDeprecated() { return isDeprecated(this.classDoc) ; } /** * 注释对象{@code doc} 中如果有 '@deprecated' 注解则返回{@code true},否则返回{@code false} * @param doc */ public boolean isDeprecated(Element doc) { if(null == doc) { return false; } return elementUtils.isDeprecated(doc); } /** * 如果{@link Member}的注释中如果有 '@deprecated' 注解则返回{@code true},否则返回{@code false} * * @param member * @since 1.3.0 */ public boolean isDeprecated(Member member) { return isDeprecated(getMemberDoc(member)); } /** * 在{@link TypeElement}中查找与method匹配的{@link ExecutableElement}对象
* 没找到匹配的对象则返回{@code null} * @param classDoc * @param member */ private Element findMember(TypeElement classDoc,Member member) { if (null == classDoc || null == member){ return null; } if(!equalType(classDoc,member.getDeclaringClass())) { return null; } if (member instanceof Field) { return ElementFilter.fieldsIn(classDoc.getEnclosedElements()).stream().filter(e -> match(member, e)) .findFirst().orElse(null); } else if (member instanceof Method) { return ElementFilter.methodsIn(classDoc.getEnclosedElements()).stream().filter(e -> match(member, e)) .findFirst().orElse(null); } else if (member instanceof Constructor) { return ElementFilter.constructorsIn(classDoc.getEnclosedElements()).stream().filter(e -> match(member, e)) .findFirst().orElse(null); } else { throw new IllegalArgumentException(String.format( "INVALID member type %s,Field,Method or Constructor required", member.getClass().getSimpleName())); } } /** * 输出当前类的类及方法注释信息 * @param out 输出对象 */ public void output(PrintStream out) { out.println(formatComment(classDoc, false)); out.println(classDoc); for (Element e : ElementFilter.methodsIn(classDoc.getEnclosedElements()) ) { ExecutableElement method = (ExecutableElement)e; out.println(formatComment(method, true)); out.printf("%s%s\n", indent, method.toString()); } } public String output() { ByteArrayOutputStream out = new ByteArrayOutputStream(); output(new PrintStream(out)); return out.toString(); } private static final String commentBody = "/**" + NEW_LINE + "cmt */" + NEW_LINE; private static final Type typeOfDoc(Object doc){ if(doc instanceof TypeElement){ return Type.CLASS; } if(doc instanceof ExecutableElement){ return Type.METHOD; } if(doc instanceof VariableElement){ return Type.FIELD; } throw new UnsupportedOperationException(); } private Set select(final Type type,final Action action){ return Maps.filterValues(additionalTextTable.rowMap(), new Predicate>(){ @Override public boolean apply(Map input) { return (input.get(AddColumn.ACTION)==action)&& type.check((Integer)input.get(AddColumn.SCOPE)); }}).keySet(); } private final String commentText(StringBuffer buffer,String originText,Type type){ Set addText = select(type,Action.ADD); Set overwriteText = select(type,Action.OVERWRITE); Set appendText = select(type,Action.APPEND); for(String text:addText)buffer.append(text).append(NEW_LINE); for(String text:overwriteText)buffer.append(text).append(NEW_LINE); if(overwriteText.isEmpty() && !originText.isEmpty()) buffer.append(originText).append(NEW_LINE); for(String text:appendText)buffer.append(text).append(NEW_LINE); return buffer.toString(); } /** * 输出格式化的注释信息 * * @param doc * {@link com.sun.tools.javadoc.ClassDocImpl} 或 {@link com.sun.tools.javadoc.MethodDocImpl}实例 * @param needIndent 是否缩进 * @param withBody 是否添加注释体 * @param withLinePrefix 是否添行注释前缀 * @param withTags 是否添加标签 * @return */ private final String formatComment0(Element doc, boolean needIndent, boolean withBody, boolean withLinePrefix, boolean withTags) { checkNotNull(doc, "doc is null"); DocCommentTree docCommentTree = docTrees.getDocCommentTree(doc); StringBuffer buffer = new StringBuffer(); if(null != docCommentTree) { Type type = typeOfDoc(doc); commentText(buffer,render.render(docCommentTree),type); if(withTags) { for (DocTree tag : docCommentTree.getBlockTags()) { BlockTagTree blockTag=(BlockTagTree)tag; if( ! type.check(excludeTags.get("@"+blockTag.getTagName()))) buffer.append(render.withFullTag().render(blockTag)).append(NEW_LINE); } } } String cmt = buffer.toString(); if (!cmt.isEmpty()) { cmt = Pattern.compile("(\r\n|\n|\r)\\s*", Pattern.MULTILINE).matcher(cmt).replaceAll(NEW_LINE); if(withBody || withLinePrefix){ cmt = Pattern.compile("^", Pattern.MULTILINE).matcher(cmt).replaceAll(" * "); } if(withBody){ cmt = commentBody.replace("cmt", cmt); } if (needIndent) cmt = Pattern.compile("^", Pattern.MULTILINE).matcher(cmt).replaceAll(indent); } return cmt; } /** * 输出格式化的注释信息 * * @param doc * {@link Element} 实例 * @param needIndent * 是否缩进 * @return 格式的注释信息字符串 */ public final String formatComment(Element doc, boolean needIndent) { return formatComment0(doc, needIndent, true, true, true); } /** * 输出格式化的注释信息,返回以行为单位的字符串表 * * @param doc * {@link Element} 实例 * @param needIndent * 是否缩进 * @param withLinePrefix 是否添行注释前缀 * @param withTags 是否添加标签 * @return 以行为单位的格式的注释信息字符串 */ public final List formatCommentAsList(Element doc, boolean needIndent, boolean withLinePrefix, boolean withTags) { String cmtstr = formatComment0(doc, needIndent, false, withLinePrefix, withTags); if(cmtstr.isEmpty()){ return Collections.emptyList(); } return Lists.newArrayList(cmtstr.split(NEW_LINE)); } public String getClassComment(boolean withBody, boolean withLinePrefix, boolean withTags) { return formatComment0(classDoc, false,withBody,withLinePrefix, withTags); } public String getMethodComment(Method method,boolean withBody, boolean withLinePrefix, boolean withTags) { ExecutableElement doc = getMethodDoc(method); return null == doc? null : formatComment0(doc, true,withBody,withLinePrefix, withTags); } public String getFieldComment(String name,boolean withBody, boolean withLinePrefix, boolean withTags) { VariableElement doc = getFieldDoc(name); return null == doc? null : formatComment0(doc, true,withBody,withLinePrefix, withTags); } public String getClassComment() { return getClassComment(true, true, true); } public String getMethodComment(Method method) { return getMethodComment(method, true, true, true); } public String getFieldComment(String name) { return getFieldComment(name, true, true, true); } public List getClassCommentAsList(boolean needIndent, boolean withLinePrefix, boolean withTags) { return formatCommentAsList(classDoc, needIndent, withLinePrefix, withTags); } public List getMethodCommentAsList(Method method,boolean needIndent, boolean withLinePrefix, boolean withTags) { ExecutableElement doc = getMethodDoc(method); return null == doc? null :formatCommentAsList(doc, needIndent, withLinePrefix, withTags); } public List getFieldCommentAsList(String name,boolean needIndent, boolean withLinePrefix, boolean withTags) { VariableElement doc = getFieldDoc(name); return null == doc? null :formatCommentAsList(doc, needIndent, withLinePrefix, withTags); } public List getClassCommentAsList() { return getClassCommentAsList(false, true, true); } public List getMethodCommentAsList(Method method) { return getMethodCommentAsList(method,true, true, true); } public List getFieldCommentAsList(String name) { return getFieldCommentAsList(name, true, true, true); } /** * 在{@link TypeElement}中查找与method匹配的{@link ExecutableElement},返回注释信息字符串
* @param method method * @return 没有方法找则返回空字符串 * @see #getMethodDoc(Method) */ @AoWeakCacheable public String commentTextOf(Method method) { ExecutableElement methodDoc = getMethodDoc(method); if(methodDoc == null) { return ""; } DocCommentTree docCommentTree = docTrees.getDocCommentTree(methodDoc); return render.render(docCommentTree); } /** * 返回当前类的注释信息字符串 */ @AoWeakCacheable public String commentText() { DocCommentTree docCommentTree = docTrees.getDocCommentTree(classDoc); return render.render(docCommentTree); } /** * 在{@link TypeElement}中查找方法的指定参数名称的注释信息字符串
* @param method method * @param name 参数名称 * @return 输入参数为{@code null}或没有方法找或没有找到参数名则返回空字符串 * @see #getMethodDoc(Method) * @see #paramTagOf(ExecutableElement, String) */ @AoWeakCacheable public String parameterCommentOf(Method method,String name) { ParamTree paramTag = paramTagOf(getMethodDoc(method), name); return paramTag == null ? "" : render.render(paramTag); } /** * @return indent */ public static String getIndent() { return indent; } /** * @param indent * 要设置的 indent */ public static void setIndent(String indent) { if (null != indent) ExtClassDoc.indent = indent; } /** * @param excludeTags 要设置的 excludeTags */ public synchronized void setExcludeTags(Map excludeTags) { if(null != excludeTags){ excludeTags.clear(); excludeTags .putAll(excludeTags); } } public void addExcludeTag(String excludeTag,Integer scope) { if(!Strings.isNullOrEmpty(excludeTag)){ excludeTags.put(excludeTag,null ==scope?Type.ALL.mask:scope); } } public void removeExcludeTag(String excludeTag){ excludeTags.remove(excludeTag); } public void addExcludeTag(String excludeTag,Type type) { addExcludeTag(excludeTag,(null ==type?Type.ALL:type).mask); } public void addExcludeTag(String excludeTag,Collection type) { addExcludeTag(excludeTag,Type.sum(type)); } public void addExcludeTag(String excludeTag,String type) { addExcludeTag(excludeTag,Strings.isNullOrEmpty(type)?Type.ALL:Type.valueOf(type)); } public void addExcludeTagString(String excludeTag,Collection type) { if(null == type || type.isEmpty()){ addExcludeTag(excludeTag,Type.ALL); } addExcludeTag(excludeTag,Type.sumOfString(type)); } public void addExcludeTag(String excludeTag) { addExcludeTag(excludeTag,Type.ALL); } public void addExcludeTags(Map excludeTag) { if(null != excludeTag) excludeTags.putAll(excludeTag); } public void additionalText(String text,Action action, Integer scope) { if(!Strings.isNullOrEmpty(text)){ additionalTextTable.put(text, AddColumn.ACTION, action); additionalTextTable.put(text, AddColumn.SCOPE, scope); } } public void additionalText(String text,Action action, Type... type) { additionalText(text,action, (null ==type?Type.ALL.mask:Type.sum(type))); } public void additionalText(String text,String action, String type) { additionalText(text, Strings.isNullOrEmpty(action)?Action.ADD:Action.valueOf(action), null ==type?Type.ALL:Type.valueOf(type)); } public void additionalText(String text,Action action, Collection type) { additionalText(text,action, (null ==type?Type.ALL.mask:Type.sum(type))); } public void additionalText(String text,String action, Collection type) { additionalText(text, Strings.isNullOrEmpty(action)?Action.ADD:Action.valueOf(action), null ==type?Type.ALL.mask:Type.sumOfString(type)); } /** * @return classDoc */ TypeElement getClassDoc() { return classDoc; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy