arc42

© 本文档使用了https://arc42.de[arc42架构模板]的材料,该模板可以在https://github.com/arc42[https://github.com/arc42]上免费获取。

本材料是开源的,采用知识共享署名-相同方式共享 4.0 许可协议提供。它*不附带任何保证*。请自行承担风险。 arc42及其结构由Peter Hruschka博士和Gernot Starke博士提供。 AsciiDoc版本由Markus Schärtel和Jürgen Krey发起,由Ralf Müller和Gernot Starke完成和维护。

link= link=https://github.com/aim42/htmlSanityCheck/issues

link=https://github.com/aim42/htmlSanityCheck/edit/main/src/docs/arc42/hsc_arc42.adoc
link=
Note

在下文中,“HTML审查器”将缩写为kbd:[HtmlSC]

文档目标

此文档是https://github.com/arc42[arc42]文档的一个示例。

您可以为自己的项目复制此文档或其部分内容。 在这种情况下,您必须包含https://github.com/arc42[arc42]或https://github.com/aim42[aim42]的链接或引用(我们认为这是*合理使用*)。

对于实际项目,代码与文档之间的关系是过大的。

免责声明

我们绝对*不提供任何保证*,无论是对于本文档的准确性还是所描述软件的任何属性或功能。

请勿在关键情况或项目中使用此软件。

```asciidoc :numbered: :linkattrs: :experimental:

1. 介绍与目标

kbd:[HtmlSC] 应支持作者创建数字格式,包括超链接和图片等资源的集成。

1.1. 需求概述

kbd:[HtmlSC] 的总体目标是创建整洁清晰的报告,展示 HTML 文件中的错误 - 如附图所示。

width=

1.1.1. 基本用法

  1. 用户配置一个或多个 HTML 文件的位置(目录和文件名),

  2. 以及相应的图像目录。

  3. kbd:[HtmlSC] 对 HTML 进行各种检查,并

  4. 将其结果报告给控制台或作为 HTML 报告。

kbd:[HtmlSC] 可以从命令行或作为 Gradle 插件运行。

除了纯语法错误外,HTML 中可能出现许多问题,特别是与超链接、锚点和 ID 相关的问题 - 因为这些通常是手动维护的。

问题的主要来源是不良链接(技术术语:URI)。有关更多信息,请参阅 URI 背景信息

破碎的交叉引用:: 交叉引用(内部链接)可能会断开,例如由于缺少或拼写错误的链接目标。

参见 BrokenCrossReferencesChecker

缺少图像文件:: 引用的图像文件可能丢失或拼写错误。

参见 MissingImageFilesChecker

缺少本地资源:: 引用的本地资源(除图像外)可能丢失或拼写错误。

参见 MissingLocalResourcesChecker

重复的链接目标:: 链接目标可能以相同名称多次出现 - 因此浏览器无法确定所需的目标。

参见 DuplicateIdChecker

损坏的外部链接:: 外部 http 链接可能由于众多原因而损坏:拼写错误、链接目标目前离线、非法链接语法。

参见 BrokenHttpLinksChecker

图像标记中缺少 Alt 属性:: 图像缺少 alt 属性。

参见 MissingImgAltAttributeChecker

检查和报告这些错误和缺陷是 kbd:[HtmlSC] 的中心 业务需求

HTML 精神检查的重要术语(领域术语)记录在一个(小)领域模型中。

1.1.3. 通用功能

1.1.4. 精神检查的类型

1.1.5. 报告和输出要求

1.2. 质量目标

1.3. 利益相关者

1.4. URI 背景信息

利益相关者

统一资源标识符的通用结构包括以下部分: [type][://][subdomain][domain][port][path][file][query][hash]

一个示例,可视化:

uri-generic-example.png

java.net.URL 类包含对 URL 和 URI 的通用解析器。 请参阅以下片段,摘自单元测试类 URLUtilTest.groovy

通用 URI 结构

URI 用于 引用 其他资源。 对于 kbd:[HtmlSC],区分内部(== 本地)和外部引用是有用的:

  • 内部引用,又称交叉引用

  • 外部引用

1.4.1. 文档内 URI

文件… ref 可以是内部链接,也可以是没有协议的 URI…

1.4.2. 关于 URI 和 HTML 语法的参考资料

以上是您提供的 AsciiDoc 文档的翻译。希望对您有所帮助!

```asciidoc :jbake-menu: arc42 :jbake-order: 20 :jbake-rightColumnHtml: <a href="../../pdf/arc42/hsc_arc42.pdf"><i class="fa fa-file fa-fw"></i> 下载PDF</a> :numbered: :linkattrs: :experimental:

2. 约束

kbd:[HtmlSC] 应当:

  • 是跨平台的,能在主要操作系统(Windows™、Linux 和 Mac-OS™)上运行

  • 与 Gradle 构建工具集成

  • 可从命令行运行

  • 是在自由开源许可下开发的 ```

这是 Constraints 部分的中文翻译,保留了 AsciiDoc 的格式。希望这对您有所帮助!

3. 上下文

3.1. 业务上下文

业务上下文
Figure 1. 业务上下文
Table 1. 业务上下文
邻居 描述

用户

使用生成 HTML 的工具链为软件编写文档。希望确保这些 HTML 中的链接是有效的。

构建系统

本地 HTML 文件

kbd:[HtmlSC] 读取并解析本地 HTML 文件,并在其中执行健全性检查。

本地图像文件

kbd:[HtmlSC] 检查链接的图像是否存在作为(本地)文件。

外部网络资源

kbd:[HtmlSC] 可以配置为选择性地检查外部网络资源的存在。由于 Web 系统的特性,这种检查可能需要大量时间,并且可能由于网络和延迟问题导致无效结果。

3.2. 部署上下文

部署上下文
Figure 2. 部署上下文
Table 2. 部署上下文
节点 / 构件 描述

kbd:[HtmlSC] 开发所在地

kbd:[HtmlSC] 的已编译和打包版本,包括所需的依赖关系。

全球公共 构件仓库,类似于 mavenCentral。 kbd:[HtmlSC] 的二进制文件被上传到该服务器。

用于任意文档编写,以 HTML 作为输出格式。

Gradle 构建脚本配置(包括其他内容)kbd:[HtmlSC] 插件执行 HTML 检查。

详细信息请参阅 部署视图

4. 解决策略

  • 使用 Groovy 和 Java 在最小化外部依赖的情况下实现 kbd:[HtmlSC]。将此实现封装为 Gradle 插件,以便在自动化构建中使用。详情请参阅 Gradle 插件概念

5. 构建块视图

5.1. 白盒 HtmlSanityChecker

@startuml
skinparam componentStyle uml2
skinparam component {
   backgroundColor<<planned>> Khaki
   backgroundColor White
}

skinparam handwritten true

left footer

<font color=blue>HtmlSanityCheck</font>
https://github.com/aim42/htmlSanityCheck
endfooter

interface "local file system" as files
interface "external Websites" as web

rectangle "HtmlSanityCheck (Level 1)" as HSC {

[HSC Core] as core #YellowGreen
[HSC Gradle Plugin] -down-> core


[HSC Graphical UI] <<planned>> as ui
[HSC Maven Plugin]  <<planned>> as mavenplugin

[FileUtil] as futil
[NetUtil] as netutil

mavenplugin .down-> core
ui .down-> core

core -> futil
core -> netutil

futil -down-> files
netutil -down-> web

}
:docu-author: -down-> ui

[Build System] as bs


bs -down-> [HSC Gradle Plugin]
bs .down.> mavenplugin

core -down-> files
core -down-> web


@enduml
原理

我们使用了 功能分解 来分离责任:

  • CheckerCore 应封装检查逻辑和 HTML 解析/处理。

  • 所有类型的输出(控制台、HTML 文件、图形)应在单独的组件(Reporter)中处理。

  • Gradle 特定功能的实现应封装在一起。

包含的黑盒
Table 3. HtmlSanityChecker 构建块

HSC 核心

hsc 核心:HTML 解析和健全性检查、配置、报告。

HSC Gradle 插件

将 Gradle 构建工具与 kbd:[HtmlSC] 集成,使任意 Gradle 构建可以使用 kbd:[HtmlSC] 功能。

HSC Maven 插件

(计划中,尚未实现)

HSC 图形界面

(计划中,尚未实现)

接口
Table 4. HtmlSanityChecker 内部接口
接口 描述

通过 shell 使用

arc42 用户 通过命令行 shell 调用 kbd:[HtmlSC]

构建系统

当前限定为 Gradle:构建系统使用 kbd:[HtmlSC],并在构建脚本中进行配置。

本地文件系统

kbd:[HtmlSC] 需要访问多个本地文件,特别是要检查的 HTML 页面和对应的图像目录。

外部网站

为了检查外部链接,kbd:[HtmlSC] 需要通过 http HEAD 或 GET 请求访问外部站点。

5.1.1. HSC 核心(黑盒)

意图/责任

HSC 核心包含执行各种健全性检查的核心功能。它将 HTML 文件解析为类似 DOM 的内存表示形式,然后用于执行实际的检查。

接口
Table 5. HSC 核心 接口
接口(从 - 到) 描述

命令行接口 → 检查器

使用 #AllChecksRunner 类。

Gradle 插件 → 检查器

通过标准 Gradle 插件公开 kbd:[HtmlSC],如 Gradle 用户指南中所述。

文件
  • org.aim42.htmlsanitycheck.AllChecksRunner

  • org.aim42.htmlsanitycheck.HtmlSanityCheckGradlePlugin

5.2. 构建块 - Level 2

5.2.1. HSC 核心(白盒)

白盒
Figure 3. HSC 核心(白盒)
原理

该结构遵循严格的功能分解:

  • 解析和处理 HTML 输入

  • 检查

  • 收集检查结果

包含的黑盒
Table 6. HSC 核心 构建块

检查器

抽象类,以模板模式形式使用。应为所有检查算法创建子类。

AllChecksRunner

不同 Checker 实例的外观。提供(基于参数的)命令行接口。

[ResultsCollector]

收集所有检查结果。其接口 Results 包含在 白盒描述

Reporter

将检查结果报告给控制台或 HTML 文件。

HtmlParser

封装 HTML 解析,提供在(解析的)HTML 中搜索的方法。

Suggester

在检查出问题时,通过比较有问题的元素与 HTML 文件中的元素来提供替代建议(当前尚未实现)。

5.2.2. Checker 和 xyzChecker 子类

抽象 Checker 提供了不同检查算法的统一接口(public void check())。它基于 可扩展检查算法的概念

5.3. 构建块 - Level 3

5.3.1. ResultsCollector(白盒)

白盒
Figure 4. 结果收集器(白盒)
原理

此结构遵循检查层次结构 - 即管理以下检查结果:

  1. 多个页面/文档,每个页面包含:

  2. 单个页面,每个页面包含许多

  3. 页面内的单个检查

包含的黑盒
Table 7. ResultsCollector 构建块

每次运行的结果

可能包含多个 HTML 页面/文档的结果。

单个页面的结果

单个页面的结果

单个检查的结果

单个类型检查的结果(例如,缺少图像检查)

发现

单个发现(例如,“图像 logo.png 缺失”)。可以包含建议和(计划中的未来版本)负责的 HTML 元素。

接口 Results

Result 接口被所有客户端(特别是 Reporter 子类、图形和命令行客户端)用于访问检查结果。它由三个不同的 API 组成,用于整体 RunResults、单个页面结果 PageResults 和单个检查结果 CheckResults。请参阅下面来自 Groovy 源代码的接口定义:

```groovy .Interface RunResults


Interface PageResults

Interface CheckResults

6. 运行时视图

注意:由于实现非常简单,对于该系统来说并不合适。

7. 部署视图

Deployment
Figure 5. 部署
Table 8. Deployment
节点 / 构件 描述

hsc 插件二进制

kbd:[HtmlSC] 的编译版本,包含所需的依赖项。

hsc-development

kbd:[HtmlSC] 的开发所在地

构件仓库 (Bintray)

用于存储二进制构件的全球公共“云”仓库,类似于 mavenCentral。 kbd:[HtmlSC] 的二进制文件会上传到此服务器。

hsc 用户计算机

任意文档的编写地,输出格式为 HTML。

build.gradle

Gradle 构建脚本,配置 kbd:[HtmlSC] 插件来检查 一些文档

[img-deployment]中显示的三个节点(计算机)通过互联网连接。

检查程序将:

  • 被捆绑为一个单独的 JAR 文件,

  • 被上传到 Bintray 仓库,

  • 可在 Gradle 构建文件中引用,

  • 提供一个带参数和选项的 main 方法,以便可以从命令行调用所有检查。

8. 技术和横切概念

8.1. HTML 检查领域模型

link=https://github.com/aim42/htmlSanityCheck/edit/main/src/docs/arc42/chapters/chap-08-checking-domain.adoc
link=
HTML 检查领域模型
Figure 6. HTML 检查领域模型
Table 9. 领域模型
术语 描述

Anchor

用于创建链接的 HTML 元素。包含形式为 <a href="link-target"> 的链接目标。

Cross Reference

文档中一部分到同一文档内另一部分的链接。是 →Internal Link 的一种特殊形式,具有同一文档中的 →Link Target。

External Link

到另一个页面或另一个域的资源的链接。

Finding

由 →Checker 在 →Html Page 中发现的问题描述。

Html Element

HTML 页面(文档)由 HTML 元素组成,例如 <a href="link target"><img src="image.png"> 等。参见 W3-Consortium

Html Page

单个 HTML 块,通常被视为单个文件。应符合标准的 HTML 语法。最低要求:我们的 HTML 解析器可以成功解析此页面。包含 →Html Elements。也称为 Html Document

id

文档中特定部分的标识符,例如 <h2 id="#someHeader">。通常用于描述 →Link Targets。

Internal Link

指向同一页的另一部分或同一域的另一页的链接。也称为 Local Link

Link

→Html Page 中任何引用,可以显示或激活此文档的另一部分(→Internal Link)或另一个文档、图像或资源(可以是 →Internal(本地)或 →External Link)。每个链接从 Link Source 导向 Link Target

Link Target

任何 →Link 的目标,例如标题或 →Html Document 的任何部分、任何内部或外部资源(通过 URI 标识)。由 →id 表示。

Local Resource

本地文件,可以是其他 Html 文件或其他类型(例如 pdf、docx)

Run Result

对多个页面(至少一页)进行检查的整体结果。

Single Page Result

单个 →Html Page 的所有检查的集合。

URI

统一资源标识符。在 RFC-2396 中定义。关于链接语法和语义的最终权威来源。

8.2. Gradle 插件概念和开发

link=https://github.com/aim42/htmlSanityCheck/edit/main/src/docs/arc42/chapters/chap-08-gradle-plugin.adoc
link=

你绝对应该阅读原始的 Gradle 用户指南,了解自定义插件开发。

为了实现 所需的 Gradle 集成,我们实现了一个简洁的包装,就像 Gradle 用户指南中所描述的那样。

class HtmlSanityCheckPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.task('htmlSanityCheck',
                type: HtmlSanityCheckTask,
                group: 'Check')
    }
}

8.2.1. 目录结构和所需文件

|-htmlSanityCheck
   |  |-src
   |  |  |-main
   |  |  |  |-org
   |  |  |  |  |-aim42
   |  |  |  |  |  |-htmlsanitycheck
   |  |  |  |  |  |  | ...
   |  |  |  |  |  |  |-HtmlSanityCheckPlugin.groovy // <1>
   |  |  |  |  |  |  |-HtmlSanityCheckTask.groovy
   |  |  |  |-resources
   |  |  |  |  |-META-INF                          // <2>
   |  |  |  |  |  |-gradle-plugins
   |  |  |  |  |  |  |-htmlSanityCheck.properties  // <3>
   |  |  |-test
   |  |  |  |-org
   |  |  |  |  |-aim42
   |  |  |  |  |  |-htmlsanitycheck
   |  |  |  |  |  |  | ...
   |  |  |  |  |  |  |-HtmlSanityCheckPluginTest
   |
  1. 实际插件代码:HtmlSanityCheckPlugin.groovyHtmlSanityCheckTask.groovy groovy 文件

  2. Gradle 期望在 META-INF 中找到插件属性

  3. 包含实际实现类名称的属性文件:implementation-class=org.aim42.htmlsanitycheck.HtmlSanityCheckPlugin

8.2.2. 从构建文件传递参数到插件

待完成

8.2.3. 构建插件

插件代码本身使用 gradle 构建。

8.2.4. 上传到公共存档

8.2.5. 有关创建 Gradle 插件的更多信息

尽管在 Gradle 用户指南中描述了编写插件,但在 Code4Reference 教程中提供了一个清晰解释的示例。

8.3. 灵活的检查算法

link=https://github.com/aim42/htmlSanityCheck/edit/main/src/docs/arc42/chapters/chap-08-checking-algorithms.adoc
link=

kbd:[HtmlSC] 使用模板方法模式来实现灵活的检查算法:

模板方法在操作中定义了算法的骨架,并将一些步骤推迟到子类中执行。
— https://sourcemaking.com/design_patterns/template_method

我们通过在一个操作中定义检查算法的骨架,将具体的检查算法步骤推迟到子类中来实现这一点。

不变的步骤是在抽象基类中实现的,而变体检查算法必须由子类提供。

Template method "performCheck"
模板方法
Figure 7. 模板方法概述
Table 10. Template Method
组件 描述

Checker

_抽象_基类,包含模板方法 check() 和公共方法 performCheck()

[MissingImageFilesChecker]

检查引用的本地图像文件是否存在

[MissingImgAltAttributeChecker]

检查是否存在没有 alt 属性的图像标签

[BrokenCrossReferencesChecker]

检查页面内引用的交叉引用(链接)是否存在

[DuplicateIdChecker]

检查任何 ID 是否有多个定义

[MissingLocalResourcesChecker]

检查引用的其他资源是否存在

[BrokenHttpLinksChecker]

检查外部链接是否有效

[IllegalLinkChecker]

检查链接是否违反了 HTML 链接语法

8.3.1. MissingImageFilesChecker

满足需求 R-1.

检查在 <img src="someFile.jpg"> 中引用的图像文件是否真的存在于本地文件系统中。

检查图像的一个小问题是它们的路径:考虑以下 HTML 片段(来自文件 testme.html):

<img src="./images/one-image.jpg">

这个图像文件("one-image.jpg")必须相对于包含相应 HTML 文件的目录。

因此,"one-image.jpg" 的预期绝对路径必须从测试的 HTML 文件的绝对路径中确定。

我们使用通常的 Java API 检查文件是否存在,但必须进行一些 目录运算 以获取 absolutePathToImageFile

File f = new File( absolutePathToImageFile );
if(f.exists() && !f.isDirectory())

8.3.2. MissingImgAltAttributeChecker

满足需求 R-6.

简单的语法检查:迭代所有 <img> 标签,检查图像是否具有 alt 标签。

8.3.3. BrokenCrossReferencesChecker

满足需求 R-2.

交叉引用是文档内部链接,在 HTML 锚标签的 href="link-target" 中没有像 httphttpsftptelnetmailtofile 等前缀。

只有以 # 开头的链接应该被考虑,例如 <a href="#internalLink">

8.3.4. DuplicateIdChecker

满足需求 R-4.

章节,特别是标题,可以通过添加 id="#xyz" 元素作为链接目标,产生如下示例的 HTML 标题。

如果相同的链接目标被多次定义,就会出现问题(如下所示)。

<h2 id="seealso">First Heading</h2>
<h2 id="seealso">Second Heading</h2>
<a href="#seealso">重复定义 - 现在我该去哪里?</a>

8.3.5. MissingLocalResourcesChecker

满足需求 R-3.

当前限制:

不进行引用带锚点的深度检查,形式如下:

<a href="api/Artifact.html#target">GroupInit</a>

包含本地(文件)引用和内部锚点 #target

请参阅问题 #252(误报)和 #253(应该检查深度链接)。

8.3.6. BrokenHttpLinksChecker

满足需求 R-9.

问题在于网络问题、延迟和 HTTP 返回代码。此检查器已计划,但目前尚未实现。

8.3.7. IllegalLinkChecker

满足需求 R-5.

此检查器已计划,但目前尚未实现。 :jbake-menu: - :numbered: :linkattrs: :experimental:

8.4. 封装 HTML 解析

link=https://github.com/aim42/htmlSanityCheck/edit/main/src/docs/arc42/chapters/chap-08-html-encapsulation.adoc
link=

我们使用简单的包装类将第三方 HTML 解析器 (https://jsoup.org) 封装在具有特定于不同检查算法的接口中。

8.5. 灵活的报告

kbd:[HtmlSC] 允许不同的输出格式:

  • 格式(HTML 和文本)和

  • 目标(文件和控制台)

报告子系统使用模板方法模式来允许不同的输出格式(例如控制台和 HTML)。报告的总体结构始终相同:

图形客户端可以使用报告子系统的 API 以任意格式显示报告。

(通用和抽象的)报告在抽象的 Reporter 类中实现如下:

/**
 * 报告的主要入口点 - 当请求报告时调用
 * 使用模板方法将具体实现委托给子类
*/
    public void reportFindings() {
        initReport()            // <1>
        reportOverallSummary()  // <2>
        reportAllPages()        // <3>
        closeReport()           // <4>
    }
//
    private void reportAllPages() {
        pageResults.each { pageResult ->
            reportPageSummary( pageResult ) // <5>
            pageResult.singleCheckResults.each { resultForOneCheck ->
               reportSingleCheckSummary( resultForOneCheck )  // <6>
               reportSingleCheckDetails( resultForOneCheck )  // <7>
               reportPageFooter()                             // <8>
        }
    }
  1. 初始化报告,例如创建并打开文件,复制 css、javascript 和图像文件。

  2. 创建总体概要,包括总体成功百分比和所有已检查页面的列表及其成功率。

  3. 遍历所有页面

  4. 写入报告页脚 - 在 HTML 报告中还创建回到顶部链接

  5. 对于单个页面,报告检查和问题的数量以及成功率

  6. 对于该页面上的每个单个检查,报告摘要和

  7. 单个检查的所有详细结果。

  8. 对于每个已检查的页面,创建页脚、分页符或类似的内容以在视觉上区分页面。

8.5.1. 美化报告输出

  • HtmlReporter 显式地生成 css 类,并与 html 元素一起使用,基于从 Gradle JUnit 插件中重用的 css 样式。

  • 样式表、jQuery JavaScript 库的缩小版本以及一些图标在报告生成时从 jar 文件复制到报告输出目录。

  • 对返回顶部箭头/按钮进行样式设置是通过 JavaScript 与一些 css 样式的组合完成的,详见 https://www.webtipblog.com/adding-scroll-top-button-website/

8.5.2. 将所需资源复制到输出目录

在创建 HTML 报告时,我们需要将所需的资源文件(css、JavaScript)复制到输出目录。

适当的复制方法是从 Gradle 源代码中重新使用的。

9. 设计决策

9.1. 推迟对外部链接的检查

这些检查被推迟到以后的版本中。

9.2. 使用 jsoup 进行 HTML 解析

为了检查 HTML,我们将其解析为内部(类似 DOM 的)表示形式。 为此任务,我们使用了 jsoup HTML 解析器,这是一个没有外部依赖的开源解析器。

从 jsoup 网站引用:

jsoup 是一个用于处理真实 HTML 的 Java 库。 它提供了一个非常方便的 API,用于提取和操作数据,使用了 DOM、CSS 和类似 jQuery 的方法。
决策目标

通过使用提供对文件的 DOM 树的访问和查找方法的现有 API,以编程方式检查 HTML。

决策标准
  • 尽量减少依赖,使 kbd:[HtmlSC] 二进制文件尽可能小。

  • 提供访问器和查找器方法,在 DOM 树中查找图像、链接和链接目标。

备选方案
  • HTTPUnit:用于 Web 应用程序和网站的测试框架。它的主要焦点是 Web 测试,并且受到大量依赖的影响。

  • jsoup:一个纯粹的 HTML 解析器,没有任何依赖项,并且具有丰富的 API,以类似 DOM 的语法访问所有 HTML 元素。

查看 kbd:[HtmlSC] 如何实现 HTML 解析的详细信息,请参阅 HTML 封装概念

9.3. 使用 Jaro-Winkler 距离进行字符串相似性检查

小型的 java-string-similarity 库(由 Ralph Allen Rice 制作)包含了几种相似性计算算法的实现。由于它*不可用*作为公共二进制文件, 我们使用了源码,主要是:

net.ricecode.similarity.JaroWinklerStrategyTest
net.ricecode.similarity.JaroWinklerStrategy
Note
相似性比较的实际实现已被推迟到 kbd:[HtmlSC] 的后续发布。

10. 术语表

有关重要术语的解释,请参阅domain model