刚刚上次弄完了一个坑爹的任务,这次我领导又给我一个让人脑瓜子疼的任务了。
基本上客户他在验收我们系统的时候,都会要求我们编写相关的文档,这次也不例外。
只是这次的客户要求我们给出接口文档。不仅是要整个controller的rest接口文档,还要给出service层的接口,连工具类都要有。这不是在为难我胖虎么。
想我辛辛苦苦给全部controller方法都加上了swagger注解,想我辛辛苦苦把整个项目全部方法都加上了完美的javadoc注释,想我关键代码都留了逻辑注释,
为什么还要我给你弄个word文档。
抱怨归抱怨,该干的活还是要干的,谁让这该死的钱真香。
但是要怎么弄呢?这是个很严重的问题,毕竟我的项目方法没一千都有一百,总不能一个个给他Ctrl C + Ctrl V吧,那我的弄到何年何月。
swagger的我可以找工具导出成word文档,可是service层的呢?我的工具类呢?
有没有现成的工具呢?问下度娘,~~~~~~~~~成功摸鱼半天,毫无收获
那能不能像上次那样用正则自己搞呢?试一哈 ~~~~~~~~~~~~~ 成功摸鱼半天,失败了,有些代码的文档注释太坑爹了,方法内用文档注释,搞事情吧,怎么还有部分前面的*号都没了。
idea不是可以导出javadoc文档么?能不能导出word呢?试一哈 ~~~~~~~~~~~~成功摸鱼半天,毫无办法,不过我找到了一个神奇的方法 com.sun.tools.javadoc.Main.execute(initAgrs); 有没有觉得很眼熟,没错,这就是jdk自己的javadoc工具。
先让我们来膜拜下前辈的文章:
https://blog.csdn.net/10km/article/details/78252586
原理其实很简单,javadoc 这个jdk的 tools.jar 中的包的类已经帮我们解析好了java文件,我们需要做的仅仅是重写一个Doclet,然后在它里面处理下javadoc给我弄好的RootDoc就行,剩下就只剩操作RootDoc它了。
先看下javadoc这个包先
其中我们将直接用到com.sun.tools.javadoc.Main这个类
那我们需要做啥呢?最简单的就3步。
1、调用com.sun.tools.javadoc.Main.execute()这个方法,传入参数
2、写一个自己的Doclet类。
3、拿到数据后,自己解析class类型、方法名、类名、方法注释、类注释、字段注释、参数注释............然后爱生成HTML、爱生成Word、爱生成Excel,随你们喜欢。
大概怎么用说完了,展示下我的生成后的效果吧,我没调样式,毕竟用poi操作word真的是难受
上代码:
项目结构:(一定要有tools.jar,他在你们jdk的lib目录里,另外POI操作word的相关包要导齐全,不然各种坑)
pom.xml主要就是为了引入POI的包
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.hongcheng</groupId>
<artifactId>javadoc_generator</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>javadoc_generator</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties> <dependencies> <dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.0.0</version>
</dependency> </dependencies>
</project>
ClassComment.java用来保存类数据的实体
package com.hongcheng.javadoc_generator.entity; import java.util.List; /**
* java类的相关信息
*/
public class ClassComment {
/** 类的全类名 */
private String className;
/** 类的简单类名 */
private String simpleClassName;
/** 类注释 */
private String classComment;
/** 字段相关信息 */
private List<FieldComment> fields;
/** 方法相关信息 */
private List<MethodComment> methods; public String getClassName() {
return className;
}
public String getSimpleClassName() {
return simpleClassName;
}
public String getClassComment() {
return classComment;
}
public List<FieldComment> getFields() {
return fields;
}
public List<MethodComment> getMethods() {
return methods;
}
public void setClassName(String className) {
this.className = className;
}
public void setSimpleClassName(String simpleClassName) {
this.simpleClassName = simpleClassName;
}
public void setClassComment(String classComment) {
this.classComment = classComment;
}
public void setFields(List<FieldComment> fields) {
this.fields = fields;
}
public void setMethods(List<MethodComment> methods) {
this.methods = methods;
}
@Override
public String toString() {
return "{className: " + className + ", simpleClassName: " + simpleClassName + ", classComment: " + classComment
+ ", fields: " + fields + ", methods: " + methods + "}";
} }
FieldComment.java用来保存字段和参数的实体
package com.hongcheng.javadoc_generator.entity; /**
* java类中字段的相关信息
*/
public class FieldComment {
/** 字段类型 */
private String clasz;
/** 类的简单类名 */
private String simpleClassName;
/** 字段注释 */
private String fieldComment;
/** 字段名 */
private String fieldName;
/** 默认值,必须是final修饰的基本数据类型及其包装类 */
private Object defaultValue; public String getSimpleClassName() {
return simpleClassName;
}
public void setSimpleClassName(String simpleClassName) {
this.simpleClassName = simpleClassName;
}
public String getClasz() {
return clasz;
}
public String getFieldComment() {
return fieldComment;
}
public String getFieldName() {
return fieldName;
}
public Object getDefaultValue() {
return defaultValue;
}
public void setClasz(String clasz) {
this.clasz = clasz;
}
public void setFieldComment(String fieldComment) {
this.fieldComment = fieldComment;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public void setDefaultValue(Object defaultValue) {
this.defaultValue = defaultValue;
}
@Override
public String toString() {
return "{clasz: " + clasz + ", simpleClassName: " + simpleClassName + ", fieldComment: " + fieldComment
+ ", fieldName: " + fieldName + ", defaultValue: " + defaultValue + "}";
} }
MethodComment.java用来保存方法数据的实体
package com.hongcheng.javadoc_generator.entity; import java.util.List; /**
* java类中方法的相关信息
*/
public class MethodComment {
/** 方法注释 */
private String methodComment;
/** 方法名 */
private String methodName;
/** 参数 */
private List<FieldComment> params;
/** 返回值 */
private FieldComment returnEntity; public String getMethodComment() {
return methodComment;
}
public String getMethodName() {
return methodName;
}
public List<FieldComment> getParams() {
return params;
}
public FieldComment getReturnEntity() {
return returnEntity;
}
public void setMethodComment(String methodComment) {
this.methodComment = methodComment;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public void setParams(List<FieldComment> params) {
this.params = params;
}
public void setReturnEntity(FieldComment returnEntity) {
this.returnEntity = returnEntity;
}
@Override
public String toString() {
return "{methodComment: " + methodComment + ", methodName: " + methodName + ", params: " + params
+ ", returnEntity: " + returnEntity + "}";
} }
MyDoclet.java很关键的类
package com.hongcheng.javadoc_generator; import com.sun.javadoc.Doclet;
import com.sun.javadoc.RootDoc; /**
* 用来获取javadoc解析完成后生成的语法树根节点
* */
public class MyDoclet extends Doclet {
/**
* 静态对象,用于接收javadoc解析完成后生成的语法树根节点<br>
* 在后面我们会用他来获取我们需要的数据
* */
private static RootDoc root; /**
* 在javadoc解析完java文件后,生成语法树,然后就会调用这个方法去让Doclet生成doc文档
* */
public static boolean start(RootDoc rootDoc) {
MyDoclet.root = rootDoc;
return true;
} /**
* 获取语法树的根节点
* */
public static RootDoc getRoot() {
return root;
} }
Modifier.java用来处理下哪些方法和字段才是我们需要的
package com.hongcheng.javadoc_generator; /**
* 可见性修饰符
* */
public enum Modifier{
PUBLIC,PROTECTED,PRIVATE;
}
RootClassParser.java用来解析RootDoc,转成我们自己的那三个实体类
package com.hongcheng.javadoc_generator; import java.util.LinkedList;
import java.util.List; import com.hongcheng.javadoc_generator.entity.ClassComment;
import com.hongcheng.javadoc_generator.entity.FieldComment;
import com.hongcheng.javadoc_generator.entity.MethodComment;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.ParamTag;
import com.sun.javadoc.Parameter;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.Tag;
/**
* RootClass对象的解析器,用于根据RootClass构建我们自己的ClassComment
* */
public class RootClassParser { /** 需要处理的类字段可见性,默认公有 */
private Modifier fieldModifier = Modifier.PUBLIC;
/** 需要处理的类方法可见性,默认公有 */
private Modifier methodModifier = Modifier.PUBLIC; public RootClassParser(Modifier fieldModifier,Modifier methodModifier) {
this.fieldModifier = fieldModifier;
this.methodModifier = methodModifier;
} /**
* 解析
* */
public List<ClassComment> parse(RootDoc root) {
if(root == null) {
return new LinkedList<ClassComment>();
}
List<ClassComment> classComments = new LinkedList<ClassComment>();
ClassDoc[] classes = root.classes();
for (ClassDoc clasz:classes) {
ClassComment classComment = new ClassComment();
classComment.setClassName(clasz.qualifiedTypeName());
classComment.setSimpleClassName(clasz.simpleTypeName());
classComment.setClassComment(clasz.commentText());
classComment.setFields( this.parseFields(clasz.fields()));
classComment.setMethods( this.parseMethods(clasz.methods()));
classComments.add(classComment);
}
return classComments;
} /**
* 解析字段
* */
private List<FieldComment> parseFields(FieldDoc[] fields){
if(fields == null || fields.length <= 0) {
return new LinkedList<FieldComment>();
}
List<FieldComment> fieldList = new LinkedList<FieldComment>();
for (FieldDoc field : fields) {
if(!this.checkModifier(field)) {
continue;
}
FieldComment fieldComment = new FieldComment();
fieldList.add(fieldComment);
fieldComment.setClasz(field.type().qualifiedTypeName());
fieldComment.setSimpleClassName(field.type().simpleTypeName());
fieldComment.setFieldComment(field.commentText());
fieldComment.setFieldName(field.name());
fieldComment.setDefaultValue(field.constantValue());
}
return fieldList;
} /**
* 检查字段修饰语,也就是public、protected、private
* @return 如果该字段的访问权限修饰语满足我们需要的级别,那就返回true
* */
private boolean checkModifier(FieldDoc field) {
if(this.getFieldModifier().toString().equalsIgnoreCase(field.modifiers())) {
return true;
}
return false;
} /**
* 检查方法修饰语,也就是public、protected、private
* @return 如果该方法的访问权限修饰语满足我们需要的级别,那就返回true
* */
private boolean checkModifier(MethodDoc method) {
if(this.getMethodModifier().toString().equalsIgnoreCase(method.modifiers())) {
return true;
}
return false;
} /**
* 解析方法
* */
private List<MethodComment> parseMethods(MethodDoc[] methods){
if(methods == null || methods.length <= 0) {
return new LinkedList<MethodComment>();
}
List<MethodComment> methodsList = new LinkedList<MethodComment>();
for (MethodDoc method : methods) {
if(!this.checkModifier(method)) {
continue;
}
MethodComment methodComment = new MethodComment();
methodsList.add(methodComment);
methodComment.setMethodComment(method.commentText());
methodComment.setMethodName(method.name());
methodComment.setReturnEntity(this.parseMethodReturn(method));
methodComment.setParams(this.parseMethodParam(method));
}
return methodsList;
} /***
* 解析方法的返回值
* */
private FieldComment parseMethodReturn(MethodDoc method){
// 返回值
FieldComment returnEntity = new FieldComment();
returnEntity.setClasz(method.returnType().qualifiedTypeName());
returnEntity.setSimpleClassName(method.returnType().simpleTypeName());
for(Tag tag:method.tags()) {
if(tag.name().equals("@return")) {
returnEntity.setFieldComment(tag.text());
break;
}
}
return returnEntity;
} /***
* 解析方法的参数
* */
private List<FieldComment> parseMethodParam(MethodDoc method){
// 参数
List<FieldComment> params = new LinkedList<FieldComment>();
for(Parameter parameter:method.parameters()) {
FieldComment param = new FieldComment();
param.setClasz(parameter.type().qualifiedTypeName());
param.setSimpleClassName(parameter.type().simpleTypeName());
param.setFieldName(parameter.name());
for(ParamTag paramTag :method.paramTags()) {
if(paramTag.parameterName().equals(param.getFieldName())) {
param.setFieldComment(paramTag.parameterComment());;
break;
}
}
params.add(param);
}
return params;
} public Modifier getFieldModifier() {
return fieldModifier;
} public Modifier getMethodModifier() {
return methodModifier;
} public void setFieldModifier(Modifier fieldModifier) {
this.fieldModifier = fieldModifier;
} public void setMethodModifier(Modifier methodModifier) {
this.methodModifier = methodModifier;
} }
WordExport.java用来生成word文档的
package com.hongcheng.javadoc_generator; import java.io.FileOutputStream;
import java.util.List; import org.apache.poi.xwpf.usermodel.TableWidthType;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge; import com.hongcheng.javadoc_generator.entity.ClassComment;
import com.hongcheng.javadoc_generator.entity.FieldComment;
import com.hongcheng.javadoc_generator.entity.MethodComment; public class WordExport { public void export(List<ClassComment> result,String path) throws Exception {
XWPFDocument xwpfDocument = this.newWord(); for(ClassComment classComment: result) {
this.newParagraph(xwpfDocument, "类名:" + classComment.getClassName());
this.newParagraph(xwpfDocument, "说明:" + classComment.getClassComment());
// 字段
if(classComment.getFields() != null && !classComment.getFields().isEmpty()) {
XWPFTable table = this.newTable(xwpfDocument, classComment.getFields().size() + 3, 5);
this.mergeCell(table.getRow(0), 0, 4, "类名:" + classComment.getClassName());
this.mergeCell(table.getRow(1), 0, 4, "属性:");
this.setTableRowText(table.getRow(2), 0, 4, "序号","参数类型","参数名","常量值","说明");
this.setCellWidth(table.getRow(2), 0, 4, "10%","22.5%","22.5%","22.5%","22.5%"); for(int i = 0,j = 3;i < classComment.getFields().size();i++,j++ ) {
FieldComment field = classComment.getFields().get(i);
this.setTableRowText(table.getRow(j), 0, 4, String.valueOf(i + 1)
,field.getSimpleClassName()
,field.getFieldName()
,field.getDefaultValue() == null?"":field.getDefaultValue().toString()
,field.getFieldComment());
}
this.newBlankLine(xwpfDocument);
this.newBlankLine(xwpfDocument);
}
// 方法
if(classComment.getMethods() != null && !classComment.getMethods().isEmpty()) {
for(MethodComment method: classComment.getMethods()) {
XWPFTable table = this.newTable(xwpfDocument, 3, 4);
this.mergeCell(table.getRow(0), 0, 3, "类名:" + classComment.getClassName());
this.mergeCell(table.getRow(1), 0, 3, "方法名:" + method.getMethodName());
this.mergeCell(table.getRow(2), 0, 3, "方法说明:" + method.getMethodComment());
// 参数
if(method.getParams() == null || method.getParams().isEmpty()) {
this.mergeCell(table.createRow(), 0, 3, "参数:无" );
}else {
this.mergeCell(table.createRow(), 0, 3, "参数:" );
this.setTableRowText(table.createRow(), 0, 3, "序号","参数类型","参数名","说明");
this.setCellWidth(table.getRow(table.getRows().size()-1), 0, 3, "10%","25%","25%","40%");
for(int i = 0;i < method.getParams().size(); i++) {
FieldComment field = method.getParams().get(i);
this.setTableRowText(table.createRow(), 0, 3, String.valueOf(i + 1)
,field.getSimpleClassName()
,field.getFieldName()
,field.getFieldComment());
}
}
// 返回值
this.mergeCell(table.createRow(), 0, 3, "返回值:" + method.getReturnEntity().getSimpleClassName() + " " + method.getMethodComment());
this.newBlankLine(xwpfDocument);
this.newBlankLine(xwpfDocument);
}
}
this.newBlankLine(xwpfDocument);
this.newBlankLine(xwpfDocument);
this.newBlankLine(xwpfDocument);
this.newBlankLine(xwpfDocument); }
this.writeFile(xwpfDocument, path);
} /**
* 设置单元格宽度
* @param row
* @param startCell 起始单元格下标,row的单元格下标从0开始
* @param endCell 结束单元格下标
* @param percentages 各个单元格的百分百大小,例如"25.5%"
* */
private void setCellWidth(XWPFTableRow row,int startCell,int endCell,String ...percentages) {
if(percentages == null || percentages.length <= 0) {
throw new IllegalArgumentException("percentages不能为空");
}
if((endCell - startCell + 1) > percentages.length) {
throw new IllegalArgumentException("percentages的元素不够");
}
int i = 0;
for(XWPFTableCell cell: row.getTableCells()) {
cell.setWidth(String.valueOf(percentages[i++]));
cell.setWidthType(TableWidthType.PCT);
}
} /**
* 设置单元格宽度
* @param row
* @param startCell 起始单元格下标,row的单元格下标从0开始
* @param endCell 结束单元格下标
* @param sizes 各个单元格的宽度大小
* */
@SuppressWarnings("unused")
private void setCellWidth(XWPFTableRow row,int startCell,int endCell,int ...sizes) {
if(sizes == null || sizes.length <= 0) {
throw new IllegalArgumentException("sizes不能为空");
}
if((endCell - startCell + 1) > sizes.length) {
throw new IllegalArgumentException("sizes的元素不够");
}
int i = 0;
for(XWPFTableCell cell: row.getTableCells()) {
cell.setWidth(String.valueOf(sizes[i++]));
cell.setWidthType(TableWidthType.DXA);
}
} /**
* 跨行合并单元格
* @param table
* @param startRow 起始行下标,table的行下标从0开始
* @param endRow 结束行下标
* @param startCell 行内起始单元格下标,row的单元格下标从0开始
* @param endCell 行内结束单元格下标
* @param text 合并后的单元格文本
* */
@SuppressWarnings("unused")
private void mergeRow(XWPFTable table,int startRow,int endRow,int startCell,int endCell,String text) {
List<XWPFTableRow> rows = table.getRows();
for (int j = startRow; j <= endRow; j++) {
List<XWPFTableCell> tableCells = rows.get(j).getTableCells();
// 对每个单元格进行操作
for (int i = startCell; i <= endCell; i++) {
//对单元格进行合并的时候,要标志单元格是否为起点,或者是否为继续合并
if (i == startCell )
tableCells.get(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
else
tableCells.get(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);//继续合并
}
}
for (int j = startRow; j <= endRow; j++) {
List<XWPFTableCell> tableCells = rows.get(j).getTableCells();
// 对每个单元格进行操作
//对单元格进行合并的时候,要标志单元格是否为起点,或者是否为继续合并
if (j == startRow )
tableCells.get(startCell).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
else
tableCells.get(startCell).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);//继续合并
}
rows.get(startRow).getCell(startCell).setText(text);//为第1行1到4合并之后的单元格设置内容
} /**
* 合并表格单元格,针对行内的单元格进行合并
* @param row
* @param startCell 起始单元格下标,row的单元格下标从0开始
* @param endCell 结束单元格下标
* @param text 合并后的单元格文本
* */
private void mergeCell(XWPFTableRow row,int startCell,int endCell,String text) {
List<XWPFTableCell> tableCells = row.getTableCells();
// 对每个单元格进行操作
for (int i = startCell; i <= endCell; i++) {
//对单元格进行合并的时候,要标志单元格是否为起点,或者是否为继续合并
if (i == startCell)
tableCells.get(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
else
tableCells.get(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);//继续合并
}
tableCells.get(startCell).setText(text);//为第1行1到4合并之后的单元格设置内容
} /**
* 给表格一行赋值,实际设置值是包括首尾单元格的,例如startCell=0,endCell=2,实际会设置0、1、2这三个单元格
* @param row
* @param startCell 起始单元格下标,row的单元格下标从0开始
* @param endCell 结束单元格下标
* @param texts 单元格的内容,依次赋值
* */
private void setTableRowText(XWPFTableRow row,int startCell,int endCell,String ...texts) {
if(texts == null || texts.length <= 0) {
throw new IllegalArgumentException("texts不能为空");
}
if((endCell - startCell + 1) > texts.length) {
throw new IllegalArgumentException("texts的元素不够");
}
List<XWPFTableCell> tableCells = row.getTableCells();
// 对每个单元格进行操作
for (int i = startCell,j = 0; i <= endCell; i++,j++) {
tableCells.get(i).setText(texts[j]);
}
} /**
* 创建一个table
* @param xwpfDocument
* @param rowNum 行数
* @param colNum 列数
* */
private XWPFTable newTable(XWPFDocument xwpfDocument,int rowNum,int colNum) {
XWPFTable createTable = xwpfDocument.createTable(rowNum, colNum);
createTable.setWidth("100%");
createTable.setWidthType(TableWidthType.PCT);
return createTable;
} /**
* 创建一个文本行
* */
private XWPFParagraph newParagraph(XWPFDocument xwpfDocument,String text) {
XWPFParagraph createParagraph = xwpfDocument.createParagraph();
createParagraph.createRun().setText(text);
return createParagraph;
} /**
* 创建一个空行
* */
private XWPFParagraph newBlankLine(XWPFDocument xwpfDocument) {
return this.newParagraph(xwpfDocument, "");
} /**
* 创建一个word文档
* */
private XWPFDocument newWord() {
XWPFDocument xwpfDocument = new XWPFDocument();
return xwpfDocument;
} /**
* 写文件
* */
private void writeFile(XWPFDocument xwpfDocument,String path) throws Exception {
xwpfDocument.write(new FileOutputStream("C:\\Users\\HongCheng\\Desktop\\1.docx"));
xwpfDocument.close();
}
}
JavaDocReader.java用来验证参数以及调用javadoc.Main类的
package com.hongcheng.javadoc_generator; import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map; import com.hongcheng.javadoc_generator.entity.ClassComment;
import com.sun.javadoc.RootDoc;
/**
* java文件doc读取器
* */
public class JavaDocReader { /** 用于接收javadoc解析完成后生成的语法树根节点 */
private MyDoclet doclet = new MyDoclet();
/** 类路径,系统会在类路径下去找一些相关的类文件 */
private List<String> classpath ;
/** java源文件地址。可以是java源文件的绝对地址,也可以是包名。只能填写一个, */
private String sourcepath;
/** 是否递归处理子包,只有在sourcepath为包名时才起作用,subpackages不为空是才是true。默认false */
private boolean isSubpackages = false;
/** 要递归的子包 */
private String subpackages ;
/** 需要处理的类字段可见性,默认公有 */
private Modifier fieldModifier = Modifier.PUBLIC;
/** 需要处理的类方法可见性,默认公有 */
private Modifier methodModifier = Modifier.PUBLIC;
/** jdk的tools.jar的地址 */
private String jdkToolsJarPath = JavaDocReader.class.getResource("/").getPath() + "/tools.jar"; /**
* 构造函数
* @param javaPackage 目标jar包,非空必填
* @param subpackages 需要递归处理的子包,可以为空
* @param classPath 相关的jar包的地址,绝对地址,javaPackage必须要能在这些路径中找到,非空必填
* */
public JavaDocReader(String javaPackage,String subpackages,List<String> classPath) {
this.init(javaPackage, subpackages, classPath, Modifier.PUBLIC, Modifier.PUBLIC);
} /**
* 构造函数
* @param javaPackage 目标jar包,非空必填
* @param subpackages 需要递归处理的子包,可以为空
* @param classPath 相关的jar包的地址,绝对地址,javaPackage必须要能在这些路径中找到,非空必填
* @param fieldModifier 需要处理的类字段可见性,非空
* @param methodModifier 需要处理的类方法可见性,非空
* */
public JavaDocReader(String javaPackage,String subpackages,List<String> classPath
,Modifier fieldModifier,Modifier methodModifier) {
this.init(javaPackage, subpackages, classPath, fieldModifier, methodModifier);
} /**
* 构造函数
* @param javaFilePath java文件地址,非空必填,绝对路径
* @param classpath 源文件中引用的相关类的jar包地址,可选
* */
public JavaDocReader(String javaFilePath,List<String> classpath ) {
this.init(javaFilePath, null, classpath, Modifier.PUBLIC, Modifier.PUBLIC);
} /**
* 构造函数
* @param javaFilePath java文件地址,非空必填,绝对路径
* @param classpath 源文件中引用的相关类的jar包地址,可选
* @param fieldModifier 需要处理的类字段可见性,非空
* @param methodModifier 需要处理的类方法可见性,非空
* */
public JavaDocReader(String javaFilePath,List<String> classpath
,Modifier fieldModifier,Modifier methodModifier) {
this.init(javaFilePath, null, classpath, fieldModifier, methodModifier);
} /**
* 构造函数
* @param sourcepath .java源文件地址,非空。可以是java源文件的绝对地址,也可以是包名。<br>
* 如果是java源文件的绝对地址,只能填写一个地址,不能填写多个地址。<br>
* 如果是包名,该包必须能在classpath下找到,只能填写一个包名<br><br>
* @param classpath 类路径,系统会在类路径下去找一些相关的类文件,可以为空
* @param subpackages 是否递归处理子包,只有在sourcepath为包名时才起作用,可以为空
* @param fieldModifier 需要处理的类字段可见性,非空
* @param methodModifier 需要处理的类方法可见性,非空
* */
private void init(String sourcepath,String subpackages,List<String> classpath
,Modifier fieldModifier,Modifier methodModifier) { this.checkNotEmpty(sourcepath, "目标java文件不能为空");
classpath = this.checkNotEmpty(classpath)?new LinkedList<String>(classpath):new LinkedList<String>();
classpath.add(this.getJdkToolsJarPath()); this.classpath = classpath;
this.sourcepath = sourcepath;
this.subpackages = subpackages;
this.fieldModifier = fieldModifier == null?this.fieldModifier:fieldModifier;
this.methodModifier = methodModifier == null?this.methodModifier:methodModifier;
this.isSubpackages = this.checkNotEmpty(subpackages);
} /**
* 初始化参数
* @return String [] javadoc需要的参数数组
* */
private String [] initAgrs() { List<String> args = new LinkedList<String>();
args.add("-encoding");
args.add("utf-8"); args.add("-doclet");
args.add(MyDoclet.class.getName()); args.add("-docletpath");
args.add(MyDoclet.class.getResource("/").getPath()); if(this.isSubpackages()) {
args.add("-subpackages");
args.add(this.getSubpackages());
} StringBuilder sb = new StringBuilder();
for(String classpath: this.getClasspath()) {
sb.append(classpath).append(";");
}
if(sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
args.add("-classpath");
args.add(sb.toString()); if(this.fieldModifier == Modifier.PRIVATE || this.methodModifier == Modifier.PRIVATE) {
args.add("-private");
}else if(this.fieldModifier == Modifier.PROTECTED || this.methodModifier == Modifier.PROTECTED) {
args.add("-protected");
}else {
args.add("-public");
} args.add(this.sourcepath); return args.toArray(new String[args.size()]);
} /**
* 执行javadoc,解析源文件
* */
private void executeJavadoc() {
String[] initAgrs = this.initAgrs();
com.sun.tools.javadoc.Main.execute(initAgrs);
} /**
* 获取类注释信息
* @return List<ClassComment>
* */
public List<ClassComment> execute(){
this.executeJavadoc();
RootDoc root = MyDoclet.getRoot();
if(root == null) {
return new LinkedList<ClassComment>();
}
RootClassParser parser = new RootClassParser(this.getFieldModifier(),this.getMethodModifier());
List<ClassComment> parseResult = parser.parse(root);
return parseResult;
} public String getJdkToolsJarPath() {
return jdkToolsJarPath;
} public void setJdkToolsJarPath(String jdkToolsJarPath) {
this.jdkToolsJarPath = jdkToolsJarPath;
} public String getSubpackages() {
return subpackages;
} public void setSubpackages(String subpackages) {
this.subpackages = subpackages;
} public MyDoclet getDoclet() {
return doclet;
} public List<String> getClasspath() {
return classpath;
} public String getSourcepath() {
return sourcepath;
} public boolean isSubpackages() {
return isSubpackages;
} public Modifier getFieldModifier() {
return fieldModifier;
} public Modifier getMethodModifier() {
return methodModifier;
} public void setDoclet(MyDoclet doclet) {
this.doclet = doclet;
} public void setClasspath(List<String> classpath) {
this.classpath = classpath;
} public void setSourcepath(String sourcepath) {
this.sourcepath = sourcepath;
} public void setSubpackages(boolean isSubpackages) {
this.isSubpackages = isSubpackages;
} public void setFieldModifier(Modifier fieldModifier) {
this.fieldModifier = fieldModifier;
} public void setMethodModifier(Modifier methodModifier) {
this.methodModifier = methodModifier;
} @SuppressWarnings("rawtypes")
private void checkNotEmpty(Object arg,String exceptionMsg) {
if(exceptionMsg == null) {
exceptionMsg = "参数不能为空。";
}
if(arg == null) {
throw new NullPointerException(exceptionMsg);
}
if(arg instanceof String) {
String argStr = (String)arg;
if(argStr.isEmpty()) {
throw new IllegalArgumentException(exceptionMsg);
}
}else if(arg instanceof Collection) {
Collection collection = (Collection)arg;
if(collection.isEmpty()) {
throw new IllegalArgumentException(exceptionMsg);
}
}else if(arg instanceof Map) {
Map map = (Map)arg;
if(map.isEmpty()) {
throw new IllegalArgumentException(exceptionMsg);
}
}
} @SuppressWarnings("rawtypes")
private boolean checkNotEmpty(Object arg) {
if(arg == null) {
return false;
}
if(arg instanceof String) {
String argStr = (String)arg;
if(argStr.isEmpty()) {
return false;
}
}else if(arg instanceof Collection) {
Collection collection = (Collection)arg;
if(collection.isEmpty()) {
return false;
}
}else if(arg instanceof Map) {
Map map = (Map)arg;
if(map.isEmpty()) {
return false;
}
}
return true;
} }
Test.java没啥屁用的测试类,就是来看效果的
package com.hongcheng.javadoc_generator; import java.util.Collections;
import java.util.List; import com.hongcheng.javadoc_generator.entity.ClassComment; public class Test {
public static void main(String[] args) throws Exception {
String jarPath = "C:\\Users\\HongCheng\\Desktop\\11.jar";
JavaDocReader javaDocReader = new JavaDocReader( "hongcheng.code_generator"
,"hongcheng.code_generator"
,Collections.singletonList(jarPath),Modifier.PRIVATE,Modifier.PUBLIC );
List<ClassComment> execute = javaDocReader.execute(); WordExport wordExport = new WordExport();
wordExport.export(execute,"C:\\Users\\HongCheng\\Desktop\\1.docx");
}
}
这是javadoc的参数,你也可以自己设置
代码上传码云了,相关jar包也在上面
https://gitee.com/1281003978/javadoc_generator
javadoc导出成word文档的更多相关文章
-
网页导出成word文档的默认视图方式问题
网页导出成word文档的默认视图方式问题 一般保存后的word文档默认是“Web版式视图”打开,这样会给客户的感觉不是真正的word文档,必须实现打开就是“页面视图” 1. 修改<html> ...
-
php将html转成word文档下载
<meta charset="utf-8" /> <?php class word{ function start(){ ob_start(); echo '&l ...
-
ABBYY将JPEG文件转换成Word文档的方法
日常工作中处理JPEG格式的图像文件时,有时需要转换成Word文档进行编辑,市场上应用而生了很多转换工具,相信不少人听说过OCR(光学字符识别)软件,可以用来转换图像文件,而在OCR软件中, ABBY ...
-
PowerDesigner将PDM导出生成WORD文档
PowerDesigner将PDM导出生成WORD文档 环境 PowerDesigner15 1.点击Report Temlates 制作模板 2.如果没有模板,单击New图标创建.有直接双击进入. ...
-
将HTML导出生成word文档
前言: 项目开发中遇到了需要将HTML页面的内容导出为一个word文档,所以有了这边随笔. 当然,项目开发又时间有点紧迫,第一时间想到的是用插件,所以百度了下.下面就介绍两个导出word文档的方法. ...
-
如何使用ABBYY FineReader 12将JPEG文件转换成Word文档
日常工作中处理JPEG格式的图像文件时,有时需要转换成Word文档进行编辑,市场上应用而生了很多转换工具,相信不少人听说过OCR(光学字符识别)软件,可以用来转换图像文件,而在OCR软件中, ABBY ...
-
(转)WEB页面导出为Word文档后分页&;横向打印的方法
<html> <HEAD> <title>WEB页面导出为Word文档后分页&横向打印的方法 </title> < ...
-
可以把思维导图导出为word文档方便其他人查看吗?
MindManager除了强大的大纲视图编辑功能外,还拥有多种导出格式,方便大家迅速导出文件,在团队中分享自己的观点,提高团队的工作效率,本次小编使用的思维导图软件版本是MindManager 202 ...
-
C# : 操作Word文件的API - (将C# source中的xml注释转换成word文档)
这篇博客将要讨论的是关于: 如何从C#的source以及注释, 生成一份Word格式的关于各个类,函数以及成员变量的说明文档. 他的大背景如下...... 最近的一个项目使用C#, 分N个模块, 在项 ...
随机推荐
-
Javascript的二进制数据处理学习 ——nodejs环境和浏览器环境分别分析
以前用JavaScript主要是处理常规的数字.字符串.数组对象等数据,基本没有试过用JavaScript处理二进制数据块,最近的项目中涉及到这方面的东西,就花一段时间学了下这方面的API,在此总结一 ...
-
开源免费天气预报接口API以及全国所有地区代码!!(国家气象局提供) 【转】
国家气象局提供的天气预报接口 接口地址: http://www.weather.com.cn/data/sk/101010100.html http://www.weather.com.cn/data ...
-
Sql Server批量删除指定表
--批量删除以test的表开头的表 declare @name varchar(50) while(exists(select * from sysobjects where name like te ...
-
1.1&hellip;&hellip;什么是3G
移动通信技术的发展 第一代移动通信技术(1st - Generation),只能进行语音通话. 第二代移动通信技术(2nd - Generation),可以收发短信.可以上网,但速度只有几十Kbps, ...
-
(转)Qt Model/View 学习笔记 (二)——Qt Model/View模式举例
Qt Model/View模式举例 Qt提供了两个标准的models:QStandardItemModel和QDirModel.QStandardItemModel是一个多用途的model,可用于表示 ...
-
HZNU1015: 矩阵排序
http://acm.hznu.edu.cn/JudgeOnline/problem.php?id=1015 题意:把矩阵每一行都排序. (以前觉得很难的题目回头看看原来如此简单 ][]; ; i&l ...
-
HDU 5280 Senior&#39;s Array (暴力,水)
题意:给一个数列,再给一个数字p,要求p一定要替换掉数列中的一个元素,然后求最大连续子序列之和. 思路:1000*1000的复杂度,O(n*n) .就是每个都试,然后求和. #include < ...
-
Stage3D学习笔记(七):动态纹理
本章用来作为Starling的滤镜实现原理的一个补充,但是为了了解原理,我们会使用原生API进行编码. 我们知道,当我们调用drawTriangles方法时,我们的图像是绘制到后台缓冲区的,只有调用p ...
-
for循环中break与continue的区别
1.for循环 for循环是更加简洁的循环语句,大部分情况下,for循环可以代替while循环.do-while循环. for循环的格式为: for( 初始语句 ; 执行条件 ; 增量 ){循环体}执 ...
-
Mysql报错 Cannot load from mysql.proc
Auth: Jin Date: 20140716 mysql --default-character-set utf8 -h127.0.0.1 -uroot -p < account-20140 ...