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

com.xnx3.doc.JavaDoc Maven / Gradle / Ivy

There is a newer version: 1.16
Show newest version
package com.xnx3.doc;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.xnx3.FileUtil;
import com.xnx3.ScanClassUtil;
import com.xnx3.SystemUtil;
import com.xnx3.UrlUtil;
import com.xnx3.doc.bean.ClassBean;
import com.xnx3.doc.bean.MethodBean;
import com.xnx3.doc.bean.ParamBean;
import com.xnx3.doc.javadoc.JavaDocBean;
import com.xnx3.doc.javadoc.JavaDocMethodBean;
import com.xnx3.doc.javadoc.JavaDocUtil;
import com.xnx3.net.HttpResponse;
import com.xnx3.net.HttpUtil;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

/**
 * JavaDoc 生成接口文档
 * @author 管雷鸣
 *
 */
public class JavaDoc {
	private String packageName;	//要搜索的包名,如 com.xnx3.wangmarket ,会自动搜索这个包下所有符合的自动生成文档
	public String templatePath = "http://res.zvo.cn.obs.cn-north-4.myhuaweicloud.com/javadoc/v1.8/";	//模板所在路径,如 http://res.zvo.cn/javadoc/template/ 这个目录下有 template.html、style.css、javadoc.js
	
	/******* 生成的文档中的一些默认值 *******/
	public String name = "API文档";		//文档名字,如 云商城用户端API文档,不设置默认为 API文档
	public String version = "1.0"; //当前文档对应你系统的版本,默认是1.0,这里是你当前系统的版本
	public String domain = "http://localhost:8080";		//API接口请求域名,不设置默认是 http://localhost:8080,这里是第一次打开文档,没有设置请求域名时,默认的域名
	public String token = "";		//第一次打开文档时,默认的token值,不设置默认为 “” 空字符串
	public String welcome = "";		//会在index.html中显示,作为入口欢迎页的说明显示。可以将一些文档通用性说明放到这里。可设置为html格式(CSS直接写到里面)以使之更美观。
	
	/**
	 * java源文件所在的文件夹路径,里面的路径如:
	 * H:\git\wm\
	 */
	public static List javaSourceFolderList = new ArrayList();
	
	/**
	 * 创建 JavaDoc 接口文档对象
	 * @param packageName 要搜索的包名,如 com.xnx3.wangmarket ,会自动搜索这个包下所有符合的自动生成文档
	 */
	public JavaDoc(String packageName) {
		this.packageName = packageName;
		
		String[] subProjectName = {"wm","xnx3_util","xnx3_weixin","wangmarket_shop","wangmarket"};
		
		File file = new File(SystemUtil.getCurrentDir());
		for (int i = 0; i < subProjectName.length; i++) {
			String subJarProjectPath = file.getParentFile().getAbsolutePath()+File.separator+subProjectName[i]+File.separator;
			JavaDoc.javaSourceFolderList.add(subJarProjectPath);
		}
	}
	
	
	public static void main(String[] args) {
		JavaDoc doc = new JavaDoc("com.xnx3.demo");
		doc.javaSourceFolderList.add("elseProject");
		doc.generateHtmlDoc();
	}
	
	/**
	 * 生成 HTML DOC 文档
	 */
	public void generateHtmlDoc() {
		List list = searchController();
		
		//过滤一些如request、model 这种的无用的参数
		for (int i = 0; i < list.size(); i++) {
			ClassBean classBean = list.get(i);
			for(int m = 0; m < classBean.getMethodList().size(); m++) {
				MethodBean mb = classBean.getMethodList().get(m);
					
				List removeKey = new ArrayList();
				for (Map.Entry entry : mb.getParams().entrySet()) {
					if("HttpServletRequest".equalsIgnoreCase(entry.getValue().getType())) {
						removeKey.add(entry.getKey());
					}else if("Model".equalsIgnoreCase(entry.getValue().getType())) {
						removeKey.add(entry.getKey());
					}
				}
				for (int rk = 0; rk < removeKey.size(); rk++) {
					mb.getParams().remove(removeKey.get(rk));
				}
			}
		}
		
		/**** 拉下最新的模板、css、js相关 ****/
		TemplateUtil template = new TemplateUtil(this);
		String apiTemplate = template.getTemplate();
		
		
		//创建存放 html的目录
		String path = SystemUtil.getCurrentDir();
		String htmldocPath = path+"/htmldoc/";
		File file = new File(htmldocPath);
		file.mkdir();
		Log.log("自动创建doc文档存放目录: "+htmldocPath);
		
		/**** 生成具体api的文档 ****/
		for (int i = 0; i < list.size(); i++) {
			ClassBean classBean = list.get(i);
			List methodList = classBean.getMethodList();
			for(int m = 0; m "+methodBean.getApiUrl()+".html");
			}
		}
		
		//生成 style.css
//		FileUtil.write(htmldocPath+"style.css", cssHr.getContent());
		//生成 htmldoc.js
//		FileUtil.write(htmldocPath+"htmldoc.js", jsHr.getContent());
		
		/**** 生成文档目录 ****/
		String indexTemplate = template.getIndex();
		List> outlineList = new ArrayList>();
		for (int i = 0; i < list.size(); i++) {
			Map mm = new HashMap();
			List> methodMapList = new ArrayList>();
			
			ClassBean classBean = list.get(i);
			List methodList = classBean.getMethodList();
			for(int m = 0; m map = new HashMap();
				map.put("urlFile", methodBean.getUrlFile());
				map.put("getApiUrl", methodBean.getApiUrl());
				map.put("commentText", getFirstLine(methodBean.getCommentText()));
				map.put("methodName", methodBean.getMethodName());
				map.put("html", replaceHtmlFileName(methodBean.getApiUrl())+".html");
//				methodMap.put(classBean.getClassName()+"."+methodBean.getMethodName()+".html", map);
				methodMapList.add(map);
			}
			
			//首页因为要只显示第一行备注,所以进行换行判断
			mm.put("apiList", methodMapList);
			mm.put("commentText", getFirstLine(classBean.getCommentText()));
			mm.put("author", classBean.getAuthor());
			mm.put("urlPath", classBean.getUrlPath());
			outlineList.add(mm);
		}
		indexTemplate = TemplateUtil.replaceAll(indexTemplate, "\\{welcome\\}", this.welcome);
		FileUtil.write(htmldocPath+"index.html", indexTemplate);
		Log.info("生成目录入口文件 > index.html");
		
		String javadocJs = TemplateUtil.replaceAll(template.getJavaDocJs(), "\\{outline\\}", JSONArray.fromObject(outlineList).toString());
		FileUtil.write(htmldocPath+"javadoc.js", javadocJs);
		Log.info("生成javadoc.js文件 > javadoc.js");
		
		//生成 style.css
		FileUtil.write(htmldocPath+"style.css", template.getStyleCss());
		Log.info("生成javadoc.js文件 > style.css");
		
		//打开htmldoc文件夹
		Log.info("正在打开文件夹...");
		SystemUtil.openLocalFolder(htmldocPath);
	}
	
	/**
	 * 多行中取第一行
	 * @param commentText
	 * @return
	 */
	private static String getFirstLine(String commentText) {
		if(commentText == null) {
			return "";
		}
		commentText = commentText.trim().split("\r|\n")[0];
		commentText = commentText.trim().split("")[0];
		return commentText;
	}

	
	/**
	 * 搜索class文件,整理出类、方法、参数的大结构(尚无javadoc注释)
	 * @return
	 */
	public List searchController() {
		List classBeanList = new ArrayList();
		
		List> classList = ScanClassUtil.getClassSearchAnnotationsName(ScanClassUtil.getClasses(this.packageName), "RequestMapping");
		for (Class controller : classList) {
        	ClassBean classBean = new ClassBean(); 
        	
        	//找到插件注册类了,进行注册插件
        	if(controller.getAnnotation(RequestMapping.class) != null){
    			//ConsoleUtil.info(controller.getName());
    			RequestMapping requestMapping_controller = (RequestMapping) controller.getAnnotation(RequestMapping.class);
//    			
    			String javaFilePath = JavaDocUtil.getJavaAbsolutePath(controller);
    			if(javaFilePath == null) {
    				//文件不存在
    				//ConsoleUtil.log("文件不存在 - "+javaFilePath);
    				continue;
    			}
    			
    			
    			JavaDocBean javaDocBean = JavaDocUtil.getJavaDoc(UrlUtil.getPath(javaFilePath), UrlUtil.getFileName(javaFilePath));
//    			ConsoleUtil.log("---"+JSONObject.fromObject(javaDocBean).toString());
    			classBean.setClassName(javaDocBean.getClassName());
    			classBean.setCommentText(javaDocBean.getCommentText());
    			
    			Map javaDocMethodMap = new HashMap();
    			List javaDocMethodBean = javaDocBean.getMethodList();
    			for (int i = 0; i < javaDocMethodBean.size(); i++) {
    				javaDocMethodMap.put(javaDocMethodBean.get(i).getMethodName(), javaDocMethodBean.get(i));
//    				System.out.println("\t"+javaDocMethodBean.get(i).getMethodName());
				}
    			
    			
    			if(requestMapping_controller.value().length < 1) {
    				Log.error(requestMapping_controller+", url path is null");
    				continue;
    			}
    			//ConsoleUtil.log("-----"+requestMapping_controller.value()[0]+"");
    			classBean.setUrlPath(requestMapping_controller.value()[0]);
    			
//    			Method[] methods = controller.getMethods();
    			Method[] methods = controller.getDeclaredMethods();
    			for (int i = 0; i < methods.length; i++) {
    				MethodBean methodBean = new MethodBean();
    				
    				Method method = methods[i];
    				RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
    				PostMapping postMapping = method.getAnnotation(PostMapping.class);
    				if(requestMapping == null && postMapping == null) {
						continue;
					}
    				if(method.getName().indexOf("$") > -1) {
            			continue;
            		}
    				
    				//判断是get请求还是post请求还是什么其他请求
    				if(requestMapping != null) {
    					if(requestMapping.method().length > 0) {
    						methodBean.setMethodType(requestMapping.method()[0].name());
    					}
    				}else if(postMapping != null) {
    					methodBean.setMethodType("POST");
    				}
    				
    				//判断方法是否有  @ResponseBody 标注
    				ResponseBody responseBody = method.getAnnotation(ResponseBody.class);
    				if(responseBody == null) {
    					//没有这个,那就是不返回json格式,忽略这个
    					continue;
    				}
    				
//					System.out.println("\t"+methods[i].getName()+"\t"+methods[i].getReturnType().getName());
					methodBean.setMethodName(methods[i].getName());
					methodBean.setReturnVoClassName(methods[i].getReturnType().getName());
					methodBean.setUrlFile(requestMapping.value()[0]);
					methodBean.setApiUrl(classBean.getUrlPath()+methodBean.getUrlFile());
					methodBean.setReturnValue(JavaDocUtil.getBeanDoc(methodBean.getReturnVoClassName()));
					
					JavaDocMethodBean javadocMethodBean = javaDocMethodMap.get(methodBean.getMethodName());
					if(javaDocMethodBean != null) {
						methodBean.setCommentText(javadocMethodBean.getCommentText());
						methodBean.setAuthor(javadocMethodBean.getAuthor());
						methodBean.setReturnCommentText(javadocMethodBean.getReturnCommentText());
					}
					
//					System.out.println("\t"+methodBean.getMethodName()+", "+requestMapping.method());
					Parameter[] ts = method.getParameters();
					for (int j = 0; j < ts.length; j++) {
						ParamBean paramBean = new ParamBean();
						paramBean.setName(ts[j].getName());
						paramBean.setType(ts[j].getType().getSimpleName());
//						System.out.println(ts[j].getName());
						if(ts[j].getAnnotations().length > 0) {
//							System.out.println("\t"+ts[j].getAnnotations()[0]);
						}
						
						//先找doc注释
						if(javadocMethodBean.getParams() != null) {
							com.xnx3.doc.javadoc.ParamBean javadocParamBean = javadocMethodBean.getParams().get(paramBean.getName());
							if(javadocParamBean != null) {
								paramBean.setCommentText(javadocParamBean.getCommentText());
							}
						}
						
						//再找 springmvc 的 @requestparam。  如果 doc注释跟这个冲突,以这个为准,注释的优先低
						RequestParam rp = ts[j].getAnnotation(RequestParam.class);
						if(rp != null) {
//							System.out.println(rp.required());
							if(rp.defaultValue() != null && rp.defaultValue().length() > 0) {
								paramBean.setDefaultValue(rp.defaultValue());
							}
							paramBean.setRequired(rp.required());
						}
						
						
						methodBean.getParams().put(paramBean.getName(), paramBean);
					}
					//判断哪些是 doc文档中(javadocMethodBean.getParams()) 有的,但是在 springmvc传参 (method.getParameters())中没有的
					for (Entry docEntry : javadocMethodBean.getParams().entrySet()) {
						
						//继续遍历 springmvc的 @requestParam
						boolean find = false;	//是否发现相同的了
						for (Entry requestParamMap : methodBean.getParams().entrySet()) {
							if(requestParamMap.getKey().equals(docEntry.getKey())) {
								//发现相同,判断下一个
								find = true;
								break;
							}
						}
						if(find) {
							continue;
						}
						
						//没有发现相同,发现javadoc有,但是springmvc的 @requestParam 么有,那也加入进参数文档
						ParamBean pb = new ParamBean();
						pb.setName(docEntry.getValue().getName());
						pb.setCommentText(docEntry.getValue().getCommentText());
						methodBean.getParams().put(pb.getName(), pb);
					}
					
					
					classBean.getMethodList().add(methodBean);
				}
        	}
        	
        	classBeanList.add(classBean);
        }
        return classBeanList;
	}
	
	
	public static boolean haveResponseBody(Annotation[] annotations) {
		for (int j = 0; j < annotations.length; j++) {
//			PluginRegister plugin = (PluginRegister) c.getAnnotation(PluginRegister.class);
			Class annotation = annotations[j].annotationType(); 
//			System.out.println(annotations[j]);
//			System.out.println(annotation.get);
		}
		
		return false;
	}
	
	/**
	 * 获取某个类及其所有父类的所有 field (private、provate、public的都会获取)
	 * @param cls
	 * @return
	 */
	public static Field[] getAllFields(Class cls){
		List fieldList = new ArrayList<>();
		while (cls != null){
			
			fieldList.addAll(new ArrayList<>(Arrays.asList(cls.getDeclaredFields())));
			cls = cls.getSuperclass();
		}
		Field[] fields = new Field[fieldList.size()];
		fieldList.toArray(fields);
		return fields;
	}
	
	/**
	 * 将api url中的\ /替换为. 免得创建不了文件
	 * @param name 传入如 /app/user/updateHead.json
	 * @return 返回如 app.user.updateHead.json
	 */
	public static String replaceHtmlFileName(String apiUrl) {
		String name = apiUrl.replaceAll("/", ".").replaceAll("\\\\", ".");
		if(name.indexOf(".") == 0) {
			name = name.substring(1, name.length());
		}
		return name;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy