1、)/P001/minAccountmodel诸多领域,特别是电子商务领域,存在很多基于 XML 语言的产品。企业集成,数据交换以及转换工具等都涉及 XML2XML 的转换工作。那么如何解析转换另一种 XML 表示的函数计算表达式呢?本文试图解决这一问题。回页首简单介绍另一种的 XML 表示的函数计算表达式在清单 2 中,我们简单介绍了另一种 XML 表示的函数计算表达式,其应用于一款基于 XML 语法的电子表单产品中。窥豹可见一斑,本文同样适用于其它基于 XML 语法的函数计算表达式。清单 2. XForms 的函数计算表达式calculationfunc name=IfThenLesscel
2、l name=DateTo /DateFrom/funcAlertboolean value=Falsestring value=This date must come after the date you have entered in the Date From fieldplainTextIf DateTo DateFrom Then False With Alert This date must come after the date you have entered in the Date From field End/calculationMinAbsSummarySubtract
3、Demo1Demo2number value=100Min(Abs(Summary),Demo1-Demo2, 100)在清单 2 中 标签用以标识函数或操作符。其 name 的属性值既是函数或操作符的名称。cell 标签,读者可以理解为用以标识函数计算表达式中的变量;而 , 等标签则是标识函数计算表达式中的常量内容。当我们需要解析函数计算表达式的时候 , 我们都有什么办法呢?比如在上面的代码片段中,直接解析 元素的内容似乎是一个办法。直接解析 Min(Abs(Summary), Demo1-Demo2, 100) 就已经得到了正确的结果。然而,解析含有大量嵌套的条件判断语句的函数计算表达式,
4、如解析第一个 元素中的内容就变得非常困难了。当我们要想进一步分析并处理 元素中的函数计算表达式的时候,表达式已经丢失了很多重要的原始信息(比如说类型信息),这样处理起来就难上加难了。有什么更好的办法么?我们可以尝试解析除了 元素以外的以 元素为根的 XML 代码片段中的信息。建立用以解析函数计算表达式的二叉树数据结构模型仔细分析一下清单 2 所示的函数计算表达式,我们不难得出其数据结构模型,如图 1 所示 :图 1. 函数计算表达式的树模型我们可以通过 XML 解析器(DOM, SAX, StAX etc.)解析 XML 得到基于以树为数据结构的内存模型。但是遍历以二叉树为数据结构的内存模型要
5、比以树为数据结构的内存模型更方便明了(读者更为熟悉)。在图 2 中,我们把以树为数据结构的模型转换为对应的以二叉树为数据结构的模型(结点的孩子结点为该结点的左孩子结点;结点的兄弟结点为该结点的右孩子结点)。图 2. 函数计算表达式的二叉树(BinTree)模型清单 3. XML 结点对应的内存模型public class CalNode public CalNode left; public CalNode right; private String value; private String type; / Three kinds of type: cell, func, const pub
6、lic CalNode(String type, String value) this.type = type; this.value = value; public CalNode(CalNode left, String type, String value, CalNode right) this.left = left; this.right = right; public String getValue() return value; public void setValue(String value) public String getType() return type;在清单
7、3 中,我们创建 XML 结点对应的内存模型类 CalNode。该类非常简单,包括左孩子结点,右孩子结点,结点本身的值以及结点的类型。根据这种函数计算表达式所涉及的 XML 元素的标签值(tag)结点的类型可分为三种:func,函数和操作符;cell,函数计算表达式中的变量 ; const, 表达式中所涉及的字符串或数值常量(当我们遍历二叉树的时候,我们需要根据结点的类型进行相应的逻辑处理。)拿上面的例子来说,当解析 的时候,就生成了一个值为 Min, 类型是 func 的 calNode 类的实例对象。清单 4. 应用 DOM 解析 XML 生成以二叉树为数据结构的内存模型private C
8、alNode createCalBinTree(Node root) String rootName = ParserHelper.validateSID(XMLUtil.getAttribute(Element) root, Constants.ATTR_NAME); /For and xxxx elements within if (StringHelper.isEmpty(rootName) /isEmpty 方法判断是否为空 rootName = ParserHelper.validateComputeContent(XMLUtil.getAttribute( (Element) ro
9、ot, Constants.ATTR_VALUE); String type = root.getNodeName(); / 结点的类型 if (!CalConstants.CELL.equals(type) & !CalConstants.FUNC.equals(type) type = CalConstants.CONST; / 如果类型不是 func 或 cell,则设置为 const CalNode rootNode = new CalNode(null, type, rootName, null); / 生成 CalNode 类的实例对象 Node firstNode = root.
10、getFirstChild(); / 拿到结点 root 的第一个孩子(DOM API) if (firstNode != null) / 递归遍历第一个孩子结点,生成 CalNode 的实例对象。 CalNode firstCalNode = createCalBinTree(firstNode); rootNode.left = firstCalNode; / 把第一个孩子结点设置为该结点的左孩子结点/ 考虑该结点的其它孩子结点 Node pNode = firstNode;CalNode qCalNode = firstCalNode;while (pNode.getNextSiblin
11、g() != null) Node nextNode = pNode.getNextSibling(); if (nextNode ! CalNode nextCalNode = createCalBinTree(nextNode); / 递归遍历其它孩子结点 qCalNode.right = nextCalNode; / 设置为其上一个兄弟结点的右孩子结点 /loop for org.w3c.dom.Node pNode = nextNode; /loop for CalNode qCalNode = nextCalNode; return rootNode;如清单 4 所示,createC
12、alBinTree 方法递归生成以二叉树为数据结构的内存模型(一棵 CalNode 类的实例对象树)。我们只要拿到对象树的根,就可以对其进行遍历处理了。应用栈中序遍历并预处理函数计算表达式清单 5 是一个使用栈的二叉树中序遍历算法。GoFarLeft 函数用来返回当前结点 t 的最左孩子结点。清单 5. 二叉树中序非递归算法(应用栈)private TreeNode GoFarLeft(TreeNode t, StackTreeNode S) if (t = null) return null; while (t.left != null) S.push(t); t = t.left; ret
13、urn t;/ inorder iterative scanprivate void Inorder_I(TreeNode t, void visit(Elem c) Stack S=new Stack(); t = GoFarLeft(t,S); / continue until t is NULL while(t ! visit(t.data); if (t.right!=null) t = GoFarLeft(t.right,S); else if ( !S.isEmpty( ) t = S.pop(); else t = null; / we are done在本文中,我们应用此算法预
14、处理函数表达式。比如,我们想把函数表达式转换成 XForms 的函数表达式,而我们在遍历此二叉树的过程中,发现某一个函数在 XForms 规范中是不支持的,比如上面清单 1 中的 Alert 函数,那么我们就可以结束此此遍历操作。当然,更多的预处理情况是根据业务逻辑的实际需要。比如本文的函数转换逻辑,我们希望知道函数表达式是否包含些特殊的函数,比如说 Sum 函数,此函数的包含与否直接影响处理逻辑的实现。中序非栈递归遍历并生成 XForms 函数计算表达式在应用清单 5 的非递归算法预处理函数计算表达式之后,我们要应用中序遍历的递归算法真正处理和解析函数计算表达式。在解析的过程中,加入 XFo
15、rms 函数计算表达式的相关逻辑,从而完成解析转换工作。在图 3 中,我们应用流程图来描述中序非栈递归遍历的解析处理过程。图 3. 解析处理函数计算机表达式的流程图如上图所示,流程图根据 rootNode 的值是否为函数,操作符而分为三部分。第一部分就是对函数计算表达式中的函数进行处理。第二部分就是递归处理表达式中的操作符。最后处理表达式中的常量和变量。其源代码如下:清单 6. 中序非栈递归遍历并解析函数表达式public void createXFormsBinding(CalNode rootNode, StringBuffer str) / 参数 str 用来存放解析结果 if (roo
16、tNode = null) / 结点为空,返回 return; String foName = rootNode.getValue(); / 获取结点的值 / 如果类型是 func , 并且是合法的函数 if ( rootNode.getType().equals(CalConstants.FUNC) & FunctionConstants.isValidFNFunction(foName) if (rootNode.left = null) / 该结点的左孩子结点是空的时候,逻辑操作如下: if (FunctionConstants.NOW.equals(foName) str.append
17、(now(); else if () else / 该结点是带有参数的函数 / 特殊函数的处理(如:IfThen 和 IFT) if (FunctionConstants.IFTHEN.equals(foName) | FunctionConstants.IFT.equals(foName) else if() / 其它特殊的函数的处理逻辑 else / 非特殊函数的处理 str.append(FunctionConstants.getMappedFunction(foName) + ( /$NON-NLS-1$ / 加左括号 / 递归调用,用以处理函数的参数 createXFormsBind
18、ing(rootNode.left, str); / 右孩子信息 CalNode p = rootNode.left; CalNode q = null; if(p! q = rootNode.left.right; while (p != null & q !, /$NON-NLS-1$ / 参数用逗号分隔 createXFormsBinding(q, str); / 递归调用,处理该参数 q = q.right; / 加右括号) /$NON-NLS-1$ / 如果类型是 func , 并且是合法的操作符 else if (rootNode.getType().equals(CalConst
19、ants.FUNC) & OperatorConstants.isValidFNOperator(foName) / 若没有左孩子结点,返回 if(rootNode.left=null) / 如果是乘,除以及求模等操作符,需要进一步判断,根据需要对左右孩子树加括号 if (OperatorConstants.DIV.equals(foName) | OperatorConstants.MULTIPLY.equals(foName)|) / 处理操作符的左孩子树 if (OperatorConstants.isValidFNOperator(rootNode.left .getValue()&
20、) else / 取操作符的名字 + OperatorConstants.getMappedXFDLOperator(foName) + / 处理操作符的右孩子树 if (rootNode.left.right != null & OperatorConstants.isValidFNOperator(rootNode.left.right createXFormsBinding(rootNode.left.right, str); / 对 % 操作符的逻辑处理 else if (OperatorConstants.PERCENT.equals(foName) / 对其它特殊操作符的逻辑处理
21、else if (OperatorConstants.XXX.equals(foName)|) / 对非 func 类型进行处理(cell 类型以及表达式中的常量) else if (rootNode.getType().equals(CalConstants.CELL) / 转成 XPATH 表达式的中结点./ + foName); /$NON-NLS-1$ else / 表达式中的常量处理 + foName + /$NON-NLS-1$ /$NON-NLS-2$ 在清单 6 中,我们比较详尽的给出了中序非栈递归遍历二叉树的算法,根据 CalNode 实例对象的不同类型分别进行处理。 fun
22、c 类型既可能是函数,也可能是操作符。我们需要加以判断进而分别处理。在函数或操作符的处理逻辑中,又存在一些特殊的函数(比如说条件判断 IfThen 等函数)和一些特殊的操作符(比如 操作符),我们同样需要区分处理。对于 cell 类型的 CalNode 类的实例对象,他们是函数计算表达式中的变量,在解析的过程中需要根据实际的逻辑需要,执行相应的逻辑操作。本文中把其转换为 Xpath 的结点形式。比如表达式中 Summary 结点被转为: ./Summary。const 类型的常量的逻辑处理相对比较容易。在本文中,仅仅是把常量类型加上单引号。如果执行此函数,Min(Abs(Summary),De
23、mo1-Demo2, 100) 这样的函数计算表达式可能会被转换为 IBM Lotus Forms 中如清单 7 所示的 XForms 绑定语句中 calculate 属性的值:清单 7. 生成 XForms 的函数计算表达式 min (abs(./Summary), ./Demo1 - ./Demo2, 100 )/P001/mvNode小结本文主要介绍了如何建立并应用二叉树的数据结构,遍历解析 XML 表示的函数计算表达式。在遍历的过程中,读者可以根据实际的需要加入相关的处理逻辑。本文涉及的用以演示的处理逻辑是函数计算表达式的转换,目的是把这种函数计算表达式转换为 IBM Lotus Forms 产品所支持的函数计算表达式(即:XForms 的函数计算表达式)。如果处理逻辑非常复杂,比如,需要结合父结点来处理子结点的逻辑。建议在清单 3 所示的内存模型中加入一个指向父结点的句柄(线索二叉树)。除此之外,本文所说的方法还可以应用到更多的应用场景,在此不一一叙述。
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1