javadoc提取工具_使JavaDoc保持最新状态的工具

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提取工具

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本程序用java编写,运行的时候需要JDK1.5或以上环境,无需安装。程序通过分析博客园博 客源码来生成一些必要的数据,可能在以后使用当中出现爬取不了的情况,可能是博客园的源码 结构修改了。程序只是用于学习之用,严禁用于非法目的而照成博客园服务器过载。 由于生成PDF的时候需要依赖字库,所以在打包程序的时候把一些必要的字库已经放到程序中 去了。可能在生成一些PDF文件的时候出现乱码问题,那是因为里面缺少需要的字库,如遇到这 个问题,请和本人联系:wyphao.2007@163com fonts文件夹是生成pdf文件依赖的字体库,如果生成的pdf文件是乱码,说明缺少相关的字体。 #################################################### 程序功能: 1、支持输入博客园博客用户名针对性下载 2、支持选择保存下载的文件 保存的结构目录为: 选择的保存路径\博客园用户名\pdf 生成的PDF文件保存路径 选择的保存路径\博客园用户名\doc 生成的DOC文件保存路径 选择的保存路径\博客园用户名\txt 生成的TXT文件保存路径 3、支持获取用户博客信息 4、支持显示用户所有的帖子列表 5、可以自己选择需要下载的帖子,有全选、反选、重置按钮 6、支持下载的文件保存为 pdf、doc、txt三种格式 7、生成的pdf、doc文件支持图片 8、支持进度显示 #################################################### 制作时间:2012年07月21日 - 2012年07月21日 制 作:w397090770 个人博客:http://blog.csdn.net/w397090770 Email :wyphao.2007@163.com 版权所有,翻版不究 ####################################################

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值