javadoc提取工具

在许多项目中,文档不是最新的。 更改代码后,很容易忘记更改文档。 原因是可以理解的。 在代码中进行更改,然后进行调试,然后希望在测试中进行更改(或者,如果您使用的是更多TDD,则以相反的顺序进行更改),然后是新功能版本的喜悦和新版本的喜悦您忘记执行更新文档的繁琐任务。

在本文中,我将显示一个示例,说明如何简化流程并确保文档至少是最新的。

工具

我在本文中使用的工具是Java :: Geci,它是一个代码生成框架。 Java :: Geci的最初设计目标是提供一个框架,在该框架中,编写代码生成器将代码注入到现有的Java源代码中或生成新的Java源文件非常容易。 因此,名称为:GEnerate Code Inline或GEnerate Code,Inject。

当我们谈论文档时,代码生成支持工具会做什么?

在框架的最高级别上,源代码只是一个文本文件。 像JavaDoc一样,文档也是文本。 源目录结构中的文档(例如markdown文件)是文本。 复制文本的一部分并将其转换到其他位置是代码生成的一种特殊形式。 这正是我们将要做的。

文档的两种用途

Java :: Geci有几种支持文档的方式。 我将在本文中描述其中之一。

方法是在单元测试中找到一些行,并在可能的转换后将内容复制到JavaDoc中。 我将使用3.9版之后的apache.commons.lang项目当前主版本的示例进行演示。 尽管有改进的余地,但该项目的文献记录非常丰富。 必须以尽可能少的人力来执行此改进。 (不是因为我们懒惰,而是因为人类的工作容易出错。)

重要的是要了解Java :: Geci不是预处理工具。 该代码进入了实际的源代码,并且得到了更新。 Java :: Geci不能消除复制粘贴代码和文本的冗余。 它对其进行管理,并确保每当导致结果发生更改时,就一遍又一遍地复制和创建代码。

Java :: Geci的一般工作方式

如果您已经听说过Java :: Geci,则可以跳过本章。 对于其他人,这里是框架的简要结构。

Java :: Geci在单元测试运行时生成代码。 Java :: Geci实际上是作为一个或多个单元测试运行的。 有一个流畅的API可以配置框架。 从本质Geci ,这意味着运行生成器的单元测试是一个断言语句,该语句创建一个新的Geci对象,调用配置方法,然后调用generate() 。 此方法generate()生成某些内容后返回true。 如果生成的所有代码与源文件中的代码完全相同,则返回false 。 如果源代码中有任何更改,则在其周围使用Assertion.assertFalse将使测试失败。 只需再次运行编译和测试。

框架收集所有配置为要收集的文件,并调用已配置和注册的代码生成器。 代码生成器与代表源文件的抽象SourceSegment对象一起使用,并且源文件中的行可能会被生成的代码覆盖。 当所有生成器完成工作后,框架将收集所有段,将其插入Source对象中,如果其中任何一个发生了重大变化,则它将更新文件。

最后,框架返回到启动它的单元测试代码。 如果更新了任何源代码文件,则返回值为true否则为false

JavaDoc中的示例

JavaDoc示例将示例自动包含在Apache Commons Lang3库中的方法org.apache.commons.lang3.ClassUtils.getAbbreviatedName()的文档中。 当前在master分支中的文档是:

 /**  *  Gets the abbreviated class name from a {@code String}.  *  *  The string passed in is assumed to be a class name - it is not checked.  *  *  The abbreviation algorithm will shorten the class name, usually without  * significant loss of meaning.  *  The abbreviated class name will always include the complete package hierarchy.  * If enough space is available, rightmost sub-packages will be displayed in full  * length.  *  *  **  *  *  *  *  *  <table><caption>Examples</caption>  <tbody>  <tr>  <td>className</td>  <td>len</td>  <td>return</td>  <td>null</td>  <td>1</td>  <td>""</td>  <td>"java.lang.String"</td>  <td>5</td>  <td>"jlString"</td>  <td>"java.lang.String"</td>  <td>15</td>  <td>"j.lang.String"</td>  <td>"java.lang.String"</td>  <td>30</td>  <td>"java.lang.String"</td>  </tr>  </tbody>  </table>  * @param className the className to get the abbreviated name for, may be {@code null}  * @param len the desired length of the abbreviated name  * @return the abbreviated name or an empty string  * @throws IllegalArgumentException if len <= 0  * @since 3.4  */ 

我们要解决的问题是自动维护示例。 要使用Java :: Geci做到这一点,我们必须做三件事:

  1. 将Java :: Geci添加为项目的依赖项
  2. 创建一个运行框架的单元测试
  3. 在单元测试中标记零件,这是信息的来源
  4. 用Java :: Geci`Segment`替换手动复制的示例文本,以便Java :: Geci将自动从测试中复制文本

相依性

Java :: Geci在Maven Central存储库中。 当前版本是1.2.0 。 它必须作为测试依赖项添加到项目中。 最终的LANG库没有依赖性,就像对JUnit或用于开发的其他任何东西都不具有依赖性。 必须添加两个显式依赖项:

 com.javax0.geci  javageci-docugen  1.2.0  test  com.javax0.geci  javageci-core  1.2.0  test 

工件javageci-docugen包含文档处理生成器。 工件javageci-core包含核心生成器。 该工件还带来了javageci-enginejavageci-api工件。 引擎本身就是框架,API本身就是API。

单元测试

第二个更改是新文件org.apache.commons.lang3.docugen.UpdateJavaDocTest 。 该文件是一个简单且非常常规的单元测试:

 /*  * Licensed to the Apache Software Foundation (ASF) ...  */  package org.apache.commons.lang3.docugen;  import *;  public class UpdateJavaDocTest {  @Test  void testUpdateJavaDocFromUnitTests() throws Exception {  final Geci geci = new Geci();  int i = 0 ;  Assertions.assertFalse(geci.source(Source.maven())  .register(SnippetCollector.builder().files( "\\.java$" ).phase(i++).build())  .register(SnippetAppender.builder().files( "\\.java$" ).phase(i++).build())  .register(SnippetRegex.builder().files( "\\.java$" ).phase(i++).build())  .register(SnippetTrim.builder().files( "\\.java$" ).phase(i++).build())  .register(SnippetNumberer.builder().files( "\\.java$" ).phase(i++).build())  .register(SnipetLineSkipper.builder().files( "\\.java$" ).phase(i++).build())  .register(MarkdownCodeInserter.builder().files( "\\.java$" ).phase(i++).build())  .splitHelper( "java" , new MarkdownSegmentSplitHelper())  .comparator((orig, gen) -> !orig.equals(gen))  .generate(),  geci.failed());  }  } 

我们在这里可以看到巨大的Assertions.assertFalse调用。 首先,我们创建一个新的Geci对象,然后告诉它源文件在哪里。 在不深入讨论细节的情况下,用户可以通过多种不同方式指定来源。 在此示例中,我们只是说,当我们使用Maven作为构建工具时,源文件通常位于这些文件中。

接下来要做的是注册不同的生成器。 生成器,尤其是代码生成器通常独立运行,因此框架不保证执行顺序。 在这种情况下,如我们稍后将看到的,这些生成器在很大程度上取决于彼此的动作。 确保它们以正确的顺序执行很重要。 该框架让我们可以分阶段实现这一目标。 询问生成器,它们需要多少个阶段,并且在每个阶段中,还询问是否需要调用它们。 每个生成器对象都是使用构建器模式创建的,在此模式中,每个生成器对象都被告知应运行哪个阶段。 当生成器配置为在阶段i运行(调用.phase(i) )时,它将告诉框架它至少需要i阶段,而对于阶段1..i-1 ,它将处于非活动状态。 这样,配置可确保生成器按以下顺序运行:

  1. 片段收集器
  2. SnippetAppender
  3. 片段正则表达式
  4. 片段修剪
  5. 片段编号器
  6. SnipetLine船长
  7. MarkdownCodeInserter

从技术上讲,所有这些都是生成器,但它们不会“生成”代码。 SnippetCollector从源文件中收集片段。 当某些示例代码需要程序不同部分的文本时, SnippetAppender可以将多个代码片段附加在一起。 SnippetRegex可以在使用正则表达式和replaceAll功能之前修改代码段(我们将在此示例中看到)。 SnippetTrim可以从行的开头删除前导制表符和空格。 当对代码进行深列表时,这一点很重要。 在这种情况下,只需将摘录片段导入文档中,就可以轻松地将实际字符从右侧的可打印区域中移出。 如果我们有一些代码在文档中引用了某些行,则SnippetNumberer可以对代码段行进行编号。 SnipetLineSkipper可以从代码中跳过某些行。 例如,您可以对其进行配置,以便跳过导入语句。

最后,可以更改源代码的真正“生成器”是MarkdownCodeInserter 。 创建它是为了将片段插入以Markdown格式的文件中,但是当需要将文本插入JavaDoc部件中时,它对于Java源文件也同样有效。

最后两个配置调用告诉框架使用MarkdownSegmentSplitHelper并使用简单的equals比较原始行和代码生成后创建的行。 SegmentSplitHelper对象可帮助框架在源代码中查找段。 在Java文件中,这些段通常是默认情况下的

线。 这有助于将手册和生成的代码分开。 在所有高级编辑器中,该编辑器折叠也是可折叠的,因此您可以专注于手动创建的代码。

但是,在这种情况下,我们将插入到JavaDoc注释内的段中。 这些JavaDoc注释可能包含一些标记,但也友好HTML,因此它们比Java更像Markdown。 尤其是,它们可能包含不会出现在输出文档中的XML注释。 在这种情况下,由MarkdownSegmentSplitHelper对象定义的片段开始于

 <!-- snip snipName parameters ... --> 

 <!-- end snip --> 

线。

必须出于非常特定的原因指定比较器。 该框架具有两个内置的比较器。 一个是默认的比较器,该比较器逐行比较每个字符。 它用于除Java外的所有文件类型。 在Java的情况下,使用了一个特殊的比较器,该比较器可以识别何时仅更改注释或仅重新格式化代码。 在这种情况下,我们将更改Java文件中注释的内容,因此我们需要告诉框架使用简单的比较器,否则它将不会影响我们进行任何更新。 (花了30分钟的时间调试为什么不先更新文件。)

最后一个调用是generate() ,它将启动整个过程。

标记代码

记录此方法的单元测试代码是org.apache.commons.lang3.ClassUtilsTest.test_getAbbreviatedName_Class() 。 外观应如下所示:

 @Test  public void test_getAbbreviatedName_Class() {  // snippet test_getAbbreviatedName_Class  assertEquals( "" , ClassUtils.getAbbreviatedName((Class<?>) null , 1 ));  assertEquals( "jlString" , ClassUtils.getAbbreviatedName(String. class , 1 ));  assertEquals( "jlString" , ClassUtils.getAbbreviatedName(String. class , 5 ));  assertEquals( "j.lang.String" , ClassUtils.getAbbreviatedName(String. class , 13 ));  assertEquals( "j.lang.String" , ClassUtils.getAbbreviatedName(String. class , 15 ));  assertEquals( "java.lang.String" , ClassUtils.getAbbreviatedName(String. class , 20 ));  // end snippet  } 

我不会在此显示原始内容,因为唯一的区别是插入了两个snippet ...end snippet行。 这些是SnippetCollector收集它们之间的线并将其存储在“ snippet store”(没有什么神秘的东西,实际上是一个很大的哈希图)中的触发器。

定义一个细分

真正有趣的部分是如何修改JavaDoc。 在本文开头,我已经介绍了今天的完整代码。 新版本是:

 /**  *  Gets the abbreviated class name from a {@code String}.  *  *  The string passed in is assumed to be a class name - it is not checked.  *  *  The abbreviation algorithm will shorten the class name, usually without  * significant loss of meaning.  *  The abbreviated class name will always include the complete package hierarchy.  * If enough space is available, rightmost sub-packages will be displayed in full  * length.  *  *  **  you can write manually anything here, the code generator will update it when you start it up  *  <table><caption>Examples</caption>  <tbody>  <tr>  <td>className</td>  <td>len</td>  <td>return</td>  <!-- snip test_getAbbreviatedName_Class regex="  replace='/~s*assertEquals~((.*?)~s*,~s*ClassUtils~.getAbbreviatedName~((.*?)~s*,~s*(~d+)~)~);/*  </tr><tr>  <td>{@code $2}</td>  <td>$3</td>  <td>{@code $1}</td>  </tr>  /' escape='~'" --><!-- end snip -->  </tbody>  </table>  * @param className the className to get the abbreviated name for, may be {@code null}  * @param len the desired length of the abbreviated name  * @return the abbreviated name or an empty string  * @throws IllegalArgumentException if len <= 0  * @since 3.4  */ 

重要的部分是15…20行的位置。 (您会看到,有时对代码段行进行编号很重要。)第15行表示段开始。 段的名称为test_getAbbreviatedName_Class并且在没有其他定义的情况下,该段也将用作要插入的代码段的名称。 但是,在插入代码段之前,它会由SnippetRegex生成器进行转换。 它将替换正则表达式的每个匹配项

 \s*assertEquals\((.*?)\s*,\s*ClassUtils\.getAbbreviatedName\((.*?)\s*,\s*(\d+)\)\); 

与字符串

 *  {@code $2}$3{@code $1} 

由于这些正则表达式位于字符串内部,因此也需要\\\\而不是单个\ 。 那会使我们的正则表达式看起来很糟糕。 因此,可以将生成器SnippetRegex配置为使用我们选择的其他一些字符,这种字符不太容易出现篱笆现象。 在此示例中,我们使用波浪号字符,并且通常可以使用。 当我们运行它时,最终结果是:

 <!-- snip test_getAbbreviatedName_Class regex="  replace='/~s*assertEquals~((.*?)~s*,~s*ClassUtils~.getAbbreviatedName~((.*?)~s*,~s*(~d+)~)~);/*  < tr >  <td>{@code $2}< /td >  <td>$3< /td >  <td>{@code $1}< /td >  < /tr >  / ' escape=' ~'" -->  *  {@code (Class) null}1{@code "" }  *  {@code String.class}1{@code "jlString" }  *  {@code String.class}5{@code "jlString" }  *  {@code String.class}13{@code "j.lang.String" }  *  {@code String.class}15{@code "j.lang.String" }  *  {@code String.class}20{@code "java.lang.String" }  <!-- end snip --> 

摘要/外卖

文档更新可以自动化。 首先,这有点麻烦。 开发人员不必复制和重新格式化文本,而是必须设置新的单元测试,标记代码段,标记段,使用正则表达式构造转换。 但是,完成后,任何更新都是自动的。 单元测试更改后,您将无法忘记更新文档。

这与创建单元测试时遵循的方法相同。 首先,创建单元测试而不是只是临时地调试和运行代码,然后查看调试器,以查看它是否确实如我们预期的那样工作,这有点麻烦。 但是,完成后会自动检查所有更新。 当影响旧代码的代码发生变化时,就不会忘记检查旧功能。

我认为文档维护应与测试一样自动化。 通常,任何可以在软件开发中自动化的东西都必须自动化,以节省工作量并减少错误。

翻译自: https://www.javacodegeeks.com/2019/09/tools-keep-javadoc-date.html

javadoc提取工具

javadoc提取工具_使JavaDoc保持最新状态的工具相关推荐

  1. 每天都有所收获_使技术保持最新状态会有所收获

    每天都有所收获 感到筋疲力尽? 没有灵感? 或更糟糕的是失业? 也许是时候更新您停滞不前的技能以接管当前公司中的出色新项目,或者使您的简历脱颖而出了. 我的技术出版事业已经持续了将近二十年,因为出版是 ...

  2. 使JavaDoc保持最新状态的工具

    在许多项目中,文档不是最新的. 更改代码后,很容易忘记更改文档. 原因是可以理解的. 在代码中进行更改,然后调试,然后希望在测试中进行更改(或者,如果您使用的是更多TDD,则以相反的顺序进行更改),然 ...

  3. 前端分离的前端开发工具_使我成为前端开发人员工作的工具和资源

    前端分离的前端开发工具 Learning front-end development can be a bit overwhelming at times. There are so many res ...

  4. ldap客户端工具_什么是性能测试?性能测试主流工具有哪些?

    性能测试的概念 一般来说,性能是一种指标,表明软件系统或构件对其及时性要求的符合程度;其次,性能是软件产品的一种特性,可以用时间来进行度量.性能的及时性用响应时间或吞吐量来衡量.响应时间是指服务器对请 ...

  5. cassandra可视化工具_一位数据科学家的私房工具清单

    作为一位万人敬仰的数据科学家,不但需要培育一棵参天技能树,私人武器库里没有一票玩得转的大火力工具也是没法在江湖中呼风唤雨的. 近日北卡来罗纳大学CTO,一位数据科学家Jefferson Heard分享 ...

  6. 2022年工具钳工(技师)最新解析及工具钳工(技师)试题及解析

    题库来源:安全生产模拟考试一点通公众号小程序 安全生产模拟考试一点通:工具钳工(技师)最新解析参考答案及工具钳工(技师)考试试题解析是安全生产模拟考试一点通题库老师及工具钳工(技师)操作证已考过的学员 ...

  7. html自动生成工具_关于STM32代码自动生成的工具的进度....

    前情提要:STM32代码自动生成工具_本想...但是...可是...所以 首先说一下那几天大家的反应,有的持观望态度,毕竟STM32CUBE很香:有的很激动,期待我快点出东西:还有的很淡定,知道我在挖 ...

  8. Vue最新状态管理工具Pinia——彻底搞懂Pinia是什么

    Pinia从了解到实际运用--彻底搞懂什么是Pinia 知识回调(不懂就看这儿!) 场景复现 什么是pinia pinia相比vuex的优势 为什么要使用pinia? 基本示例 知识回调(不懂就看这儿 ...

  9. react性能监控根据工具_高性能React:3个新工具可加快您的应用程序

    react性能监控根据工具 by Ben Edelstein 通过本·爱德斯坦 高性能React:3个新工具可加快您的应用程序 (High Performance React: 3 New Tools ...

最新文章

  1. JavaWeb课程复习资料(六)——DAO与DAOImpl层封装
  2. 【Boost】boost库asio详解4——deadline_timer使用说明
  3. 查询用户分配角色TCODE
  4. iQOO Neo 855竞速版来了:今年最后一款骁龙855 Plus手机
  5. DeepMind去年亏损27亿元,同比扩大221%,谷歌说:继续烧
  6. EDA实验课课程笔记(四)——TCL脚本语言的学习2
  7. 【转】嵌入式软件:C语言编码规范
  8. instant-ngp
  9. 卡内基梅隆大学计算机专业系,卡内基梅隆大学计算机
  10. Android AGP 7.0 适配,开始强制 JDK 11
  11. python编程求长方形的面积_Python实现计算长方形面积(带参数函数demo)
  12. 程序员是一种优秀的男朋友
  13. 无领导小组讨论题目分类
  14. 最新限量红包封面,限时领取!
  15. 一些实用的镜像网站推荐
  16. input的各种事件
  17. Java手写dubbo框架
  18. 浮点数的整数小数部分分别输出
  19. 案例|PIGOSS TOCBSM完美结合,服务于首信社保部
  20. html弹出框播放视频

热门文章

  1. Vases and Flowers HDU - 4614
  2. P4009 汽车加油行驶问题
  3. 树上启发式合并 简单例题
  4. 欢乐纪中B组周五模拟赛【2019.3.8】
  5. POJ2186-Popular Cows(流行的奶牛)【tarjan,强连通分量,图论】
  6. ssl2295-暗黑破坏神【dp练习】
  7. 【LCT】大融合(luogu 4219)
  8. 转圈游戏(luogu 1965)
  9. 如何使ArrayList 线程安全
  10. Java IO: Reader和Writer