在RainbowPortal中,当工作流开启时,需要为编辑器和/或审批者提供一种方式,以直观地比较当前生产版本与暂存版本的HTML文档的差异。这种代码不会发布在RainbowPortal中。
经过一些研究,选择了Eugene W. Myers在其论文《An O(ND) Difference Algorithm and its Variation》中提出的算法,这也是Unix Diff使用的算法。可以在此处找到文件的副本。Unix Diff的实现可以在此处找到。
总体要求可以简要概括为:
要比较的是可能包含HTML标签、HTML注释、空白符和特殊字符(如" "、"{"等)的HTML文本。文档应该作为一个整体进行比较,而不是逐行比较。重要的是文档内容的差异,因为格式已经直观显示。差异应该被突出显示。
由于是文档级比较,为了性能和方便起见,只比较文档内容,决定逐词而不是逐字符比较文档。因此,过程可以分为两部分。第一部分是解析文档以找出标签、自然词、标点符号、特殊字符和空白符等。第二部分是比较单词、标点符号和一些特殊字符以找出差异。由于比较过程中忽略的内容仍然需要用于重建文档,因此必须通过将其与相邻的单词关联起来来保留它们。
定义了一个名为Word
的类来表示解析出的单词以及它前面的标签、空白符和特殊字符。Word
类的定义如下:
internal class Word : IComparable
{
private string _word = string.Empty;
private string _prefix = string.Empty;
private string _suffix = string.Empty;
public Word() { ... }
public Word(string word, string prefix, string suffix) { ... }
...
// 用于重建无变化的单词
public string reconstruct() { ... }
// 用于重建已更改的单词
public string reconstruct(string beginTag, string endTag) { ... }
public int CompareTo(object obj) { ... }
}
解析遵循以下规则:
Word
对象的前缀或后缀字段中。Word
类的word
字段中。还定义了一个强类型集合WordsCollection
来保存解析出的单词。
定义了一个名为HtmlTextParser
的类,其中包含一个静态方法用于解析。
internal class HtmlTextParser
{
static public WordsCollection parse(string s) { ... }
}
最后,定义了一个名为Merger
的类来执行比较和合并的繁重工作:
class Merger
{
private WordsCollection _original;
private WordsCollection _modified;
private IntVector fwdVector;
private IntVector bwdVector;
public Merger(string original, string modified) { .. }
...
private MiddleSnake findMiddleSnake(Sequence src, Sequence des) { ... }
private string doMerge(Sequence src, Sequence des) { ... }
...
public string merge() { ... }
}
Merger
类的构造函数将接受HTML文本字符串,并调用HtmlTextParser.parse()
将它们解析成两个单词集合。当调用公共方法merge()
时,它将从集合中构建Sequence
对象并调用私有方法doMerge()
,而doMerge
将递归比较和合并整个集合。