利用javadoc定制自己的接口文档(二)
2016-08-21 16:46
288 查看
前言
上一篇我们介绍了doclet及其命令行选项,最后是自己自定义的doclet1代码,思路很简单,就是利用doclet读取代码在方法上面的注解,然后将这些注解的值写到模板中,最后输出到指定位置。在第一代doclet中,自己将html模板,java模板,自定义标签名称等都写在一起,耦合性极强。这一篇就是利用freemarker将其中的模板从中抽出来,于是有了第二代doclet——doclet2freemarker的简介
FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具,简而言之,就是:模板+数据模型=输出。自己之所以决定使用它,最主要的原因有以下4点:
1.它能很方便得生成各种文本:HTML,XML,PTF,JAVA源代码等
2.是轻量级的框架,不需要Sservlet环境,易于嵌入到产品中
3.可以从任何源载入模板,如本地文件,数据库等
4.可以按需生成文本:保存到本地文件;作为email发送;从web应用程序发送它返回给web浏览器
这4点简直是量身为doclet打造的:自己所写的doclet2,并没有servlet环境;数据是从doclet中拿到的;最终不仅要生成html格式的文档,还要生成java类型的前端代码。
(ps:在java领域,表现层技术主要有三种:jsp、freemarker、velocity。大家有兴趣可以查一查这三种的优缺点)
freemarker的使用
freemarker的模板是.ftl文件,在文档编译器中编辑好模板,保存时以.ftl结尾即可。freemarker的语法和标签的使用,大家可以参考:http://demojava.iteye.com/blog/800204,写得很全,我就不赘述了。freemarker的数据模型使用三种基本的对象类型:scalars(标量),hashes(哈希表),sequences(序列),除此之外,还有方法和用户自定义FTL标记两种,具体可参考
http://www.zzbaike.com/wiki/FreeMarker%E7%9A%84%E6%95%B0%E6%8D%AE%E6%A8%A1%E5%9E%8B
freemarker的合并主要是以下四个步骤:
1.创建Configuration实例,该实例负责管理FreeMarker的模板加载路径,生成模板实例。
Configuration cfg = new Configuration();
2.使用Configuration实例来生成Template实例,在这里需要指定使用的模板文件。
//指定模板路径 File file = new File("src"); //设置要解析的模板所在的目录,并加载模板文件 cfg.setDirectoryForTemplateLoading(file); //设置编码方式 cfg.setDefaultEncoding("UTF-8"); Template indexTemplate = cfg.getTemplate("index.ftl");
3.填充数据模型,数据模型就是一个Map对象。
Map indexRoot = new HashMap();
4.调用Template实例的process方法完成合并。
//将要生成的文件 File indexfile = new File(DES_DIRPATH + "2.0.html"); //得到输出流 BufferedWriter write = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(indexfile),"UTF-8")); //合并模板和数据 indexTemplate.process(indexRoot,write);
针对每个方法,自己要生成的html文档的ftl模板(文件名为:Method.ftl)如下:
<!DOCTYPE html> <html> <meta charset=utf8> <head> <title></title> </head> <style type="text/css"> body{ font: 12px/1.125 Arial, Helvetica, sans-serif; } .wiki_title{ line-height: 37px; border-bottom: 1px solid #e5e5e5; margin: 16px 0 8px 0; font-size: 20px; color: #333; font-family: "Microsoft Yahei"; font-weight: 300; } h1.wiki_title{ font-size: 24px; } a{ color: #3c7cb3; text-decoration: none; } table{ border-collapse: collapse; border-spacing: 0; } table.parameters{ border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; width: 100%; } th,td{ text-align: center; font-weight: bolder; border: 1px solid #cccccc; height: 20px; } .code_type{ text-transform: uppercase; margin-bottom: 5px; display: inline-block;* display: inline;* zoom: 1; background: #b4e3b4; border-radius: 2px; color: #008200; padding: 2px 8px; } a:hover{ text-decoration: underline; } </style> <body> <h1 class="wiki_title"> <span class="mw-headline">${uri}</span> </h1> <p>${describe}</p> <h2 class="wiki_title"> <span class="mw-headline">URL</span> </h2> <p> <span style="font-weight:600"> <a rel="nofollow" class="external free" href="">${uri}</a> </span> </p> <h2 class="wiki_title"> <span class="mw-headline" >支持格式</span> </h2> <p> <span style="text-transform:uppercase;font-weight:600">${support!"JSON"}</span> </p> <h2 class="wiki_title"> <span class="mw-headline" >HTTP请求方式</span> </h2> <p> <span style="text-transform:uppercase;font-weight:600">${requesttype!"POST"}</span> </p> <h2 class="wiki_title"> <span class="mw-headline" >请求参数</span> </h2> <table border="1" cellspacing="0" cellpadding="0" width="100%" class="parameters" style="border-color: #CCCCCC;"> <tbody> <tr> <th width="10%" style="text-align:center;font-weight:bolder;border:1px solid #cccccc">名称</th> <th width="5%" style="text-align:center;font-weight:bolder;border:1px solid #cccccc">必选</th> <th width="10%" style="text-align:center;font-weight:bolder;border:1px solid #cccccc">类型及范围</th> <th width="75%" style="text-align:center;font-weight:bolder;border:1px solid #cccccc">说明</th> </tr> <#list param as item> <tr> <td style="text-align:center;font-weight:bolder;border:1px solid #cccccc">${item.name}</td> <td style="text-align:center;border:1px solid #cccccc">${item.select}</td> <td style="text-align:left;padding-left:5px;border:1px solid #cccccc">${item.type}</td> <td style="text-align:left;padding-left:5px;border:1px solid #cccccc">${item.explain}</td> </tr> </#list> </tbody> </table> <h2 class="wiki_title"> <span class="mw-headline">返回结果</span> </h2> <div class="code_type" style="text-transform:uppercase;margin-bottom:5px;">JSON示例</div> <pre> ${returnjson} </pre> <h2 class="wiki_title"> <span class="mw-headline">返回字段说明</span> </h2> <table border="1" cellspacing="0" cellpadding="0" width="100%" class="parameters" style="border-color: #CCCCCC;"> <tbody> <tr> <th width="25%" style="text-align:left;padding-left:5px;font-weight:bolder;border:1px solid #cccccc">返回值字段</th> <th width="15%" style="text-align:left;padding-left:5px;font-weight:bolder;border:1px solid #cccccc">字段类型</th> <th width="60%" style="text-align:left;padding-left:5px;font-weight:bolder;border:1px solid #cccccc">字段说明</th> </tr> <#list returnparam as item> <tr> <td style="text-align:left;padding-left:5px;font-weight:bolder;border:1px solid #cccccc">${item.name}</td> <td style="text-align:left;padding-left:5px;border:1px solid #cccccc">${item.type}</td> <td style="text-align:left;padding-left:5px;border:1px solid #cccccc">${item.explain}</td> </tr> </#list> </tbody> </table> <h2 class="wiki_title"> <span class="mw-headline" >注意事项</span> </h2> <p>${notice!"无"}</p> </body> </html>
要生成的java代码的ftl文档(名为javaTemplate.ftl)如下:
package ${basepath}.request; import java.util.HashMap; import java.util.Map; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; import ${basepath}.LlptHttpJsonRequest; import ${basepath}.response.getTagListResponse; public class ${methodname}Request extends LlptHttpJsonRequest<${methodname}Response> { private static final String APIPATH = "${uri}"; <#list paramlist as param> private String ${param.name}; public String get${param.name?cap_first}() {return ${param.name};} public void set${param.name?cap_first}(String ${param.name}) {this.${param.name} = ${param.name};} </#list> public ${methodname}Request(Listener<${methodname}Response> listener, ErrorListener errorListener) { super(Method.POST, APIPATH, listener, errorListener); } public ${methodname}Request(int method, String partUrl, Listener<${methodname}Response> listener, ErrorListener errorListener) { super(method, partUrl, listener, errorListener); } public Class<${methodname}Response> getResponseClass() {return ${methodname}Response.class;} public String GetApiPath() {return APIPATH;} public Map<String, String> GetParameters() { Map<String, String> map = new HashMap<String, String>(); <#list paramlist as param> map.put("${param.name}",${param.name}); </#list> return map; } }
自己所写的doclet2代码(265行)如下:
import com.sun.javadoc.*; import com.sun.tools.doclets.formats.html.ConfigurationImpl; import freemarker.template.Configuration; import freemarker.template.Template; import java.io.*; import java.lang.System; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @mytag class mytag */ public class doclet2 { //类相关的注解 private static String CLASS_DESCRIBE = "description"; //类的描述,如人脉,用户,消息 private static String CLASS_URI = "uri"; //类的访问路径 //方法相关的注解 private static String METHOD_URI = "uri"; //方法访问路径 private static String METHOD_TYPE = "type"; //方法类别 private static String METHOD_DESCRIBE = "description"; //方法描述 private static String METHOD_PATH = "path"; //UE中如何找到接口 private static String METHOD_SUPPORT = "datatype"; //支持格式(json) private static String METHOD_REQUESTTYPE = "method"; //请求方式(get/post) private static String METHOD_PARAM = "param"; //传入参数描述,描述格式为:名称~必选~类型~说明(若为非必选,须说明什么情况下不选) private static String METHOD_NOTICE = "notice"; //注意事项 private static String METHOD_RETURNJSON = "returnjson"; //返回的json格式 private static String METHOD_RETURNPARAM = "returnparam"; //输出参数描述,描述格式为:名称~类型~字段说明 private static String DIRPATH="D:/api2/"; private static String BASEPATH = "com.bluemobi.bluecollar.network"; public static boolean start(RootDoc root) { try { doc(root.classes()); } catch (Exception e) { e.printStackTrace(); } return true; } private static String doc_class(ClassDoc classDoc, String TAG) { System.out.print(classDoc.containingPackage()); Tag[] Class_Describe = classDoc.tags(TAG); if (Class_Describe.length != 0) { return Class_Describe[0].text(); }else { return null; } } private static String docMethod(MethodDoc methodDoc, String TAG) { Tag[] mcts = methodDoc.tags(TAG); if (mcts.length != 0) return mcts[0].text(); else return null; } private static ArrayList<String> docMethodList(MethodDoc methodDoc, String TAG) { ArrayList<String> params = new ArrayList<String>(); Tag[] mcts = methodDoc.tags(TAG); for (int i=0; i < mcts.length ; i++) { params.add(mcts[i].text()); } return params; } /** * @mytag doc mytag * @param classDocs */ private static void doc(ClassDoc[] classDocs) throws Exception { BufferedWriter write=null; for (int i = 0; i < classDocs.length; i++) { String class_describe=doc_class(classDocs[i], CLASS_DESCRIBE); String class_uri=doc_class(classDocs[i], CLASS_URI); StringBuffer destdirname= new StringBuffer(); destdirname.append(DIRPATH).append(doc_class(classDocs[i], CLASS_URI)); MethodDoc[] methods = classDocs[i].methods(); //创建configuration对象 Configuration cfg = new Configuration(); //指定模板路径 File file = new File("."); // System.out.println(file.getAbsolutePath()); //设置要解析的模板所在的目录,并加载模板文件 cfg.setDirectoryForTemplateLoading(file); //设置编码方式 cfg.setDefaultEncoding("UTF-8"); Template indexdivTemplate = cfg.getTemplate("indexdiv.ftl"); Map indexRoot = new HashMap(); indexRoot.put("description",class_describe); List<Map> readlist = new ArrayList<>(); List<Map> writelist = new ArrayList<>(); indexRoot.put("readlist",readlist); indexRoot.put("writelist",writelist); for(int j = 0; j< methods.length; j++){ String method_uri=docMethod(methods[j], METHOD_URI); String method_path=docMethod(methods[j],METHOD_PATH); String method_type=docMethod(methods[j], METHOD_TYPE); //获取uri后即可得函数名称,API路径,构造函数, String uri = class_uri+"/"+method_uri; //获取模板 Template javaTemplate = cfg.getTemplate("javaTemplate.ftl"); Template methodTemplate = cfg.getTemplate("Method.ftl"); //封装方法的数据对象 Map MethodRoot = new HashMap(); Map JavaRoot = new HashMap(); // System.out.println(method_type); //首页数据的写入 if (method_type.equals("write")) { Map writediv = new HashMap(); writediv.put("uri",uri); writediv.put("methodpath",method_path); writelist.add(writediv); } else if (method_type.equals("read")) { Map readdiv = new HashMap(); readdiv.put("uri",uri); readdiv.put("methodpath", method_path); readlist.add(readdiv); } //如果方法的uri类似于history/personal,则将personal作为方法名称 String javaname = method_uri.substring(method_uri.lastIndexOf('/')+1,method_uri.length()); JavaRoot.put("methodname",javaname); JavaRoot.put("basepath", BASEPATH); JavaRoot.put("uri",uri); MethodRoot.put("uri", uri); MethodRoot.put("describe", docMethod(methods[j], METHOD_DESCRIBE)); MethodRoot.put("support", docMethod(methods[j], METHOD_SUPPORT)); MethodRoot.put("requesttype", docMethod(methods[j], METHOD_REQUESTTYPE)); MethodRoot.put("returnjson", docMethod(methods[j], METHOD_RETURNJSON)); MethodRoot.put("notice", docMethod(methods[j], METHOD_NOTICE)); List<Map> paramlist = new ArrayList<Map>(); List<Map> javaparamlist = new ArrayList<Map>(); MethodRoot.put("param", paramlist); JavaRoot.put("paramlist",javaparamlist); ArrayList<String> params = docMethodList(methods[j], METHOD_PARAM);//list中每一项对应一个请求参数的数据 for (int k=0; k<params.size();k++) { String data = params.get(k); //获取每一个参数数据 String[] options = data.split("~"); //拆分需要的数据 Map javaparam = new HashMap(); javaparam.put("name",options[0]); Map param = new HashMap(); param.put("name",options[0]); param.put("select",options[1]); param.put("type",options[2]); param.put("explain",options[3]); paramlist.add(param); javaparamlist.add(javaparam); } List<Map> returnparamlist = new ArrayList<Map>(); MethodRoot.put("returnparam", returnparamlist); ArrayList<String> returnparams = docMethodList(methods[j], METHOD_RETURNPARAM);//list中每一项对应一个返回参数的数据 for (int k=0; k<returnparams.size();k++) { String data = returnparams.get(k); //获取每一个参数数据 String[] options = data.split("~"); //拆分需要的数据 Map returnparam = new HashMap(); returnparam.put("name",options[0]); returnparam.put("type",options[1]); returnparam.put("explain",options[2]); returnparamlist.add(returnparam); } //每个方法对应的html文件 StringBuffer filenamepath = new StringBuffer(); filenamepath.append(destdirname.toString()); filenamepath.append("/"); filenamepath.append(method_uri); filenamepath.append(".html"); // System.out.println(filenamepath.toString()); //每个方法对应的java文件 StringBuffer javapath = new StringBuffer(); javapath.append(destdirname.toString()); javapath.append("/"); javapath.append(method_uri); javapath.append("Request.java"); //输出html文件 File writefile = new File(filenamepath.toString()); String dirMethodPath = writefile.getParent(); File dirHtmlFile = new File(dirMethodPath); if (!dirHtmlFile.exists()){ dirHtmlFile.mkdirs(); } write = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(writefile),"UTF-8")); methodTemplate.process(MethodRoot, write); write.flush(); //输出java文件 File javafile = new File(javapath.toString()); String dirJavaPath = writefile.getParent(); File dirJavaFile = new File(dirJavaPath); if (!dirJavaFile.exists()){ dirJavaFile.mkdirs(); } write = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(javafile),"UTF-8")); javaTemplate.process(JavaRoot,write); write.flush(); } StringBuffer indexnamepath = new StringBuffer(); indexnamepath.append(DIRPATH); indexnamepath.append(class_uri); indexnamepath.append(".html"); File indexfile = new File(indexnamepath.toString()); String dirIndexPath = indexfile.getParent(); File dirIndexFile = new File(dirIndexPath); if (!dirIndexFile.exists()) dirIndexFile.mkdirs(); write = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(indexfile),"UTF-8")); indexdivTemplate.process(indexRoot, write); write.flush(); } if (write != null) write.close(); } public static int optionLength(String option) { // Construct temporary configuration for check return (ConfigurationImpl.getInstance()).optionLength(option); } public static boolean validOptions(String options[][], DocErrorReporter reporter) { // Construct temporary configuration for check return (ConfigurationImpl.getInstance()).validOptions(options, reporter); } }
相关文章推荐
- 利用javadoc定制自己的接口文档(三)
- Flex中如何利用tabStyleName, firstTabStyleName, lastTabStyleName和selectedTabTextStyleName样式,定制自己的TabNavigator
- Flex中如何利用tabStyleName, firstTabStyleName, lastTabStyleName和selectedTabTextStyleName样式定制自己的TabNavigator的例子
- 利用SPM工具运行自己创建的小组件(使用common-model向后台接口请求数据)
- 利用Javadoc工具生成api文档
- 文档注释使用javadoc工具生成自己的api帮助文档
- 从零开始学Java自己利用接口和集合框架做的简单图书管理系统
- 利用javadoc生成API文档
- 利用javadoc生成API文档
- javadoc生成word接口文档
- 利用javadoc制作API文档
- Tolua使用,利用pkg文件,封装自己的lua支持的cocos2dx的接口
- 利用Debian定制适合自己的系统
- 利用Qt Assistant 定制帮助文档
- 自己整理的支付宝接口签约,测试,开发文档资料
- 利用飞信接口自己搞天气预报服务
- 利用InjectedBundle定制自己的Webkit(二)
- TSIS5.1仿真软件利用RTE接口嵌入自己的算法
- 自己生成网络后台接口并利用charles模拟Http请求和响应
- Android Studio中利用JavaDoc生成项目API文档