Board logo

标题: 为 SAX ContentHandler 构建编译器(1)消息显示与XPathParser [打印本页]

作者: look_w    时间: 2018-7-18 08:40     标题: 为 SAX ContentHandler 构建编译器(1)消息显示与XPathParser

在上两篇专栏文章之前,我启动了 HC(处理程序编译器)项目作为本专栏的一个新项目。HC 的目标是编译一个                代理内容处理程序,该代理内容处理程序使 XPath 与 SAX 解析中的特定方法(                应用处理程序 ― application handler)相匹配。            
我发现自己在进行 SAX 编程时,在一些低级的、重复性的工作(如,状态跟踪)上花费了过多时间。为了尝试从这些技术细节中解脱出来,我启动了 HC 这个项目。
如果您还没有阅读过上两篇专栏文章,则我鼓励您浏览一下那两篇文章(请参阅右侧“相关内容”中的链接),因为这个月我要实现那两篇文章中介绍过的算法。
讲得更具体些,在上篇专栏文章中,我回顾了编译所谓“确定性有限自动机(Deterministic Finite Automaton,简称 DFA)”的一些算法。DFA 是一个深受大家喜爱的算法,用来构造一个状态机来识别模式。在 HC 中,这些模式就是 XPath。
消息和显示这篇专栏文章中所涉及的大多数开发工作都是在实现上月所介绍的 DFA 构造算法。然而,在真正实现算法之前,我首先必须实现一些用于消息显示的实用类(utility class)。如果您定期阅读本专栏,则您应该很熟悉这些类中的大多数:
虽然,在整个 HC 操作中,这些类是很重要的(设想一下,如果一个编译器不能报告错误,那会是怎样?),但它们与编译 XPath 没有直接联系,并且这些类大多数都简单明了,无须额外解释。因此,在本专栏文章中,我将不再讲述这些类。相反,我希望您下载这些类,自己来看一下代码(请参阅 )。            
XPath 解析当构造 DFA 时,两个相关元素发挥了作用。从上一篇专栏文章所讨论的内容中,您可以回忆起,构造 DFA 的算法需要一棵解析树作为输入。对于 HC,该解析树表示一个 XPath。
XPathNode 在上一篇专栏文章中,我为解析树中的节点定义了一个                 HCNode 类。当实现该算法时,我意识到遗忘了两件重要的事情:            
我选择通用对象来存储匹配 XPath 时的信息,是因为不希望将解析器局限于给定的应用程序。当匹配 XPath 时,DFA 编译器是不负责确定要做什么以及如何分配优先级这些事。
至于                 followpos() ,我在类构造器中添加了一个取值(方法)和新代码:            
1
2
3
4
5
6
7
8
9
case PARENT_OF:
   // compute lastpos() and firstpos()
   Iterator iterator = left.lastpos().iterator();
   while(iterator.hasNext())
   {
      XPathNode n = (XPathNode)iterator.next();
      n.followpos.addAll(right.firstpos());
   }
   break;




如在上一篇专栏文章中所编写的,                 HCNode 封装了                 QName 。后者表示一个 XML 元素,而                 HCNode 表示 XPath 中的一个元素(所以它可能是一个 XML 元素或是一个表示“……之子”的关系)。            
在处理                 QName 时,我发现它与                 HCNode 之间的区别不是很明显。逻辑上,应由                 QName (而不是其它类)来表示 XPath 中的 XML 元素。            
为可读性起见,我选择用继承                 QName 的类来代替                 HCNode 。为了更好地反映这种用法,我将这个类的名称改为                 XPathNode 。在其它方面,                 XPathNode 与以前所介绍的                 HCNode 完全一样。更明确地讲,它有                 firstpos() 、                 lastpos() 和                 followpos() 方法。            
遗憾的是,这造成了一个问题:                 XPathNode 从                 QName 继承了                 equals() 和                 hashCode() 。这最终导致难以跟踪                 followpos() 中的错误。            
这个问题在于,                 QName 表示输入词汇表中的符号。如果两个符号有相同的名称空间 URI 和本地名称,则这两个符号是完全一样的。换句话说,                 simpara 等于                 simpara 。            
但是,对于                 XPathNode 却不是这样!它们表示 XPath 中的某一确定元素,以便                 simpara/ulink 中的                 simpara 不同于                 simpara/emphasis 中的                 simpara 。            
讲得更具体些,它们的                 followpos() 应该是不同的:在第一种情况中是                 ulink ,第二种情况则是                 emphasis 。            
我必须为                 XPathNode 重新编写                 equals() ,以便在两个不同的节点出现在不同的 XPath 时,区别对待这两个节点。出于调试目的,我额外地实现了一个用于比较的                 isSynonymousWith() 方法。这个新方法用来比较两个                 XPathNode 。            
通过递归地比较当前节点下的树,该方法可以执行更深一步的比较,或者只是执行停留在当前节点的这种快速比较。我只是打算在调试时使用这个方法,它可以让我用所期望的结果与解析的结果作比较。
在使用 XPath 解析器时,我还确定:应该将                 END_MARK ,这种标记 XPath 结束的特殊类型的                 XPathNode /                 HCNode 更好地表示为                 QName ,所以我将该常量从                 XPathNode 移到                 QName 。            
在我们还在讨论                 XPathNode /                 HCNode 时,我发现                 java.util.Set 有一个                 addAll() 方法,它与我所编写的                 merge() 是一样的。现在,我使用                 addAll() 而不使用                 merge() 。            
最后一个(但不是最不重要的),我已经为这些类改写了 JUnit 测试。可以从 CVS 资源库(请参阅  )下载                 XPathNode 及其测试。            
XPathParserXPathParser 负责实际的 XPath 解析,如 所示。如在以前专栏文章中所提到的,这个类只识别 XPath 的子集,这与我早先的决定 ― 不支持 XPath 中的条件 ― 相一致。            
这个类构造器最多有三个参数:
大多数解析在                 xpath() 方法中处理。该方法是一种普通的解析。它使用标准的                 StringTokenizer 将字符串拆成基本标记。接着,该方法对这些标记进行循环以查找 XML 元素、属性和表示“……之父”关系的分隔符(                 / )。            
在识别标记时,该方法创建                 XPathNode ,它带有相应的类型:                 ELEMENT 、                 ATTRIBUTE 和                 PARENT-OF 。必须注意绝对 XPath 指示符。            
实际上,                 XPathParser 的用户使用                 axpath() 方法的次数要比使用                 xpath() 多。                 axpath() 方法返回称之为扩张的解析树 ― 该解析树额外地带有一个表示                 END_MARK 的节点。这个额外的节点保存关于匹配 XPath 时要做什么的信息。如上面所讨论的,出于那一目的,它使用                 data 和                 priority 字段。            
我还编写了用于该解析器的 JUnit 测试程序。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0