应用二叉树解析 XML 表示的函数计算表达式.docx
- 文档编号:3505580
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:14
- 大小:56.17KB
应用二叉树解析 XML 表示的函数计算表达式.docx
《应用二叉树解析 XML 表示的函数计算表达式.docx》由会员分享,可在线阅读,更多相关《应用二叉树解析 XML 表示的函数计算表达式.docx(14页珍藏版)》请在冰豆网上搜索。
应用二叉树解析XML表示的函数计算表达式
应用二叉树解析XML表示的函数计算表达式
XForms的函数计算表达式简介
XForms是用于XML数据处理的Web表单规范,它允许您将表单的用途和外观分开。目前W3C组织正在审查XForms1.1的候选工作草案(1.0是正式的Internet推荐标准)。
IBM®LotusForms(电子表单领域的杰出产品之一)就是基于XForms和XFDL(ExtensibleFormsDescriptionLanguage)语言的,它把XForms强大的数据处理能力和XFDL语言丰富的表示能力紧密的结合在一起。
XForms的数据模型(DataModel)封装了对表单数据的一些逻辑处理操作。
如清单1所示,xforms:
bind元素拥有一个calculate属性。
该属性通过函数计算表达式来实现表单数据的处理逻辑。
清单1.XForms的函数计算表达式
model…> instance>… instance> … bindcalculate="../SubTotal+../TaxAmount+../Shipping" nodeset="instance('INSTANCE')/P001/Total"> bind> bindcalculate="if(../BudgetMeals! ='',../BudgetMeals,'0')-../ActualMeals" nodeset="instance('INSTANCE')/P001/VarianceMeals"> bind> bindcalculate="min(../aAccount,../bAccount,../cAccount)" nodeset="instance('INSTANCE')/P001/minAccount"> bind> …
model>
诸多领域,特别是电子商务领域,存在很多基于XML语言的产品。
企业集成,数据交换以及转换工具等都涉及XML2XML的转换工作。
那么如何解析转换另一种XML表示的函数计算表达式呢?
本文试图解决这一问题。
回页首
简单介绍另一种的XML表示的函数计算表达式
在清单2中,我们简单介绍了另一种XML表示的函数计算表达式,其应用于一款基于XML语法的电子表单产品中。
窥豹可见一斑,本文同样适用于其它基于XML语法的函数计算表达式。
清单2.XForms的函数计算表达式
…
Fromfield"/> "ThisdatemustcomeafterthedateyouhaveenteredintheDateFromfield" End … 在清单2中 其name的属性值既是函数或操作符的名称。 当我们需要解析函数计算表达式的时候,我们都有什么办法呢? 比如在上面的代码片段中,直接解析 直接解析 然而,解析含有大量嵌套的条件判断语句的函数计算表达式,如解析第一个 当我们要想进一步分析并处理 有什么更好的办法么? 我们可以尝试解析除了 回页首 建立用以解析函数计算表达式的二叉树数据结构模型 仔细分析一下清单2所示的函数计算表达式,我们不难得出其数据结构模型,如图1所示: 图1.函数计算表达式的树模型 我们可以通过XML解析器(DOM,SAX,StAXetc.)解析XML得到基于以树为数据结构的内存模型。 但是遍历以二叉树为数据结构的内存模型要比以树为数据结构的内存模型更方便明了(读者更为熟悉)。 在图2中,我们把以树为数据结构的模型转换为对应的以二叉树为数据结构的模型(结点的孩子结点为该结点的左孩子结点;结点的兄弟结点为该结点的右孩子结点)。 图2.函数计算表达式的二叉树(BinTree)模型 清单3.XML结点对应的内存模型 publicclassCalNode{ publicCalNodeleft; publicCalNoderight; privateStringvalue; privateStringtype;//Threekindsoftype: cell,func,const publicCalNode(Stringtype,Stringvalue){ this.type=type; this.value=value; } publicCalNode(CalNodeleft,Stringtype,Stringvalue,CalNoderight){ this.left=left; this.type=type; this.value=value; this.right=right; } publicStringgetValue(){ returnvalue; } publicvoidsetValue(Stringvalue){ this.value=value; } publicStringgetType(){ returntype; } } 在清单3中,我们创建XML结点对应的内存模型类——CalNode。 该类非常简单,包括左孩子结点,右孩子结点,结点本身的值以及结点的类型。 根据这种函数计算表达式所涉及的XML元素的标签值(tag)结点的类型可分为三种: func,函数和操作符;cell,函数计算表达式中的变量;const,表达式中所涉及的字符串或数值常量(当我们遍历二叉树的时候,我们需要根据结点的类型进行相应的逻辑处理。 )拿上面的例子来说,当解析 清单4.应用DOM解析XML生成以二叉树为数据结构的内存模型 privateCalNodecreateCalBinTree(Noderoot){ StringrootName=ParserHelper.validateSID(XMLUtil.getAttribute( (Element)root,Constants.ATTR_NAME)); //For and elementswithin if(StringHelper.isEmpty(rootName)){//isEmpty方法判断是否为空 rootName=ParserHelper.validateComputeContent(XMLUtil.getAttribute( (Element)root,Constants.ATTR_VALUE)); } Stringtype=root.getNodeName();//结点的类型 if(! CalConstants.CELL.equals(type)&&! CalConstants.FUNC.equals(type)) type=CalConstants.CONST;//如果类型不是func或cell,则设置为const CalNoderootNode=newCalNode(null,type,rootName,null);//生成CalNode类的实例对象 NodefirstNode=root.getFirstChild();//拿到结点root的第一个孩子(DOMAPI) if(firstNode! =null){ //递归遍历第一个孩子结点,生成CalNode的实例对象。 CalNodefirstCalNode=createCalBinTree(firstNode); rootNode.left=firstCalNode;//把第一个孩子结点设置为该结点的左孩子结点 //考虑该结点的其它孩子结点 NodepNode=firstNode; CalNodeqCalNode=firstCalNode; while(pNode.getNextSibling()! =null){ NodenextNode=pNode.getNextSibling(); if(nextNode! =null){ CalNodenextCalNode=createCalBinTree(nextNode);//递归遍历其它孩子结点 qCalNode.right=nextCalNode;//设置为其上一个兄弟结点的右孩子结点 //loopfororg.w3c.dom.Node pNode=nextNode; //loopforCalNode qCalNode=nextCalNode; } } } returnrootNode; } 如清单4所示,createCalBinTree方法递归生成以二叉树为数据结构的内存模型(一棵CalNode类的实例对象树)。 我们只要拿到对象树的根,就可以对其进行遍历处理了。 回页首 应用栈中序遍历并预处理函数计算表达式 清单5是一个使用栈的二叉树中序遍历算法。 GoFarLeft函数用来返回当前结点t的最左孩子结点。 清单5.二叉树中序非递归算法(应用栈) privateTreeNode GoFarLeft(TreeNode if(t==null) returnnull; while(t.left! =null){ S.push(t); t=t.left; } returnt; } //inorderiterativescan privatevoidInorder_I(TreeNode Stack t=GoFarLeft(t,S); //continueuntiltisNULL while(t! =null){ visit(t.data); if(t.right! =null) t=GoFarLeft(t.right,S); elseif(! S.isEmpty()) t=S.pop(); else t=null;//wearedone } } 在本文中,我们应用此算法预处理函数表达式。 比如,我们想把函数表达式转换成XForms的函数表达式,而我们在遍历此二叉树的过程中,发现某一个函数在XForms规范中是不支持的,比如上面清单1中的Alert函数,那么我们就可以结束此此遍历操作。 当然,更多的预处理情况是根据业务逻辑的实际需要。 比如本文的函数转换逻辑,我们希望知道函数表达式是否包含些特殊的函数,比如说Sum函数,此函数的包含与否直接影响处理逻辑的实现。 回页首 中序非栈递归遍历并生成XForms函数计算表达式 在应用清单5的非递归算法预处理函数计算表达式之后,我们要应用中序遍历的递归算法真正处理和解析函数计算表达式。 在解析的过程中,加入XForms函数计算表达式的相关逻辑,从而完成解析转换工作。 在图3中,我们应用流程图来描述中序非栈递归遍历的解析处理过程。 图3.解析处理函数计算机表达式的流程图 如上图所示,流程图根据rootNode的值是否为函数,操作符而分为三部分。 第一部分就是对函数计算表达式中的函数进行处理。 第二部分就是递归处理表达式中的操作符。 最后处理表达式中的常量和变量。 其源代码如下: 清单6.中序非栈递归遍历并解析函数表达式 publicvoidcreateXFormsBinding(CalNoderootNode,StringBufferstr){ //参数str用来存放解析结果 if(rootNode==null){//结点为空,返回 return; } StringfoName=rootNode.getValue();//获取结点的值 //如果类型是func,并且是合法的函数 if(rootNode.getType().equals(CalConstants.FUNC)&& FunctionConstants.isValidFNFunction(foName)){ if(rootNode.left==null){ //该结点的左孩子结点是空的时候,逻辑操作如下: if(FunctionConstants.NOW.equals(foName)){ str.append("now()"); }elseif(…){ … } return; }else{//该结点是带有参数的函数 //特殊函数的处理(如: IfThen和IFT) if(FunctionConstants.IFTHEN.equals(foName) ||FunctionConstants.IFT.equals(foName)){ … return; }elseif(…){//其它特殊的函数的处理逻辑 … return; }else{//非特殊函数的处理 str.append(FunctionConstants.getMappedFunction(foName) +"(");//$NON-NLS-1$//加左括号 //递归调用,用以处理函数的参数 createXFormsBinding(rootNode.left,str); } } //右孩子信息 CalNodep=rootNode.left; CalNodeq=null; if(p! =null) q=rootNode.left.right; while(p! =null&&q! =null){ str.append(",");//$NON-NLS-1$//参数用逗号分隔 createXFormsBinding(q,str);//递归调用,处理该参数 q=q.right; } //加右括号 str.append(")");//$NON-NLS-1$ //如果类型是func,并且是合法的操作符 }elseif(rootNode.getType().equals(CalConstants.FUNC)&& OperatorConstants.isValidFNOperator(foName)){ //若没有左孩子结点,返回 if(rootNode.left==null){ return; } //如果是乘,除以及求模等操作符,需要进一步判断,根据需要对左右孩子树加括号 if(OperatorConstants.DIV.equals(foName) ||OperatorConstants.MULTIPLY.equals(foName)||…){ //处理操作符的左孩子树 if(OperatorConstants.isValidFNOperator(rootNode.left .getValue())&&…){ str.append("(");//$NON-NLS-1$ createXFormsBinding(rootNode.left,str); str.append(")");//$NON-NLS-1$ }else{ createXFormsBinding(rootNode.left,str); } //取操作符的名字 str.append(""+ OperatorConstants.getMappedXFDLOperator(foName)+""); //处理操作符的右孩子树 if(rootNode.left.right! =null &&OperatorConstants.isValidFNOperator(rootNode.left.right .getValue())&&…){ str.append("(");//$NON-NLS-1$ createXFormsBinding(rootNode.left.right,str); str.append(")");//$NON-NLS-1$ }else{ createXFormsBinding(rootNode.left.right,str); } } //对%操作符的逻辑处理 elseif(OperatorConstants.PERCENT.equals(foName)){ … } //对其它特殊操作符的逻辑处理 elseif(OperatorConstants.XXX.equals(foName)||…){ … } //对非func类型进行处理(cell类型以及表达式中的常量) else{ if(rootNode.getType().equals(CalConstants.CELL)){ //转成XPATH表达式的中结点 str.append("../"+foName);//$NON-NLS-1$ }else{//表达式中的常量处理 str.append("'"+foName+"'");//$NON-NLS-1$//$NON-NLS-2$ } } } 在清单6中,我们比较详尽的给出了中序非栈递归遍历二叉树的算法,根据CalNode实例对象的不同类型分别进行处理。 func类型既可能是函数,也可能是操作符。 我们需要加以判断进而分别处理。 在函数或操作符的处理逻辑中,又存在一些特殊的函数(比如说条件判断IfThen等函数)和一些特殊的操作符(比如%操作符),我们同样需要区分处理。 对于cell类型的CalNode类的实例对象,他们是函数计算表达式中的变量,在解析的过程中需要根据实际的逻辑需要,执行相应的逻辑操作。 本文中把其转换为Xpath的结点形式。 比如表达式中Summary结点被转为: ../Summary。 const类型的常量的逻辑处理相对比较容易。 在本文中,仅仅是把常量类型加上单引号。 如果执行此函数,Min(Abs(Summary),Demo1-Demo2,100)这样的函数计算表达式可能会被转换为IBM®LotusForms中如清单7所示的XForms绑定语句中calculate属性的值: 清单7.生成XForms的函数计算表达式 bindcalculate="min(abs(../Summary),../Demo1-../Demo2,‘100’)" nodeset="instance('INSTANCE')/P001/mvNode"> bind> 回页首 小结 本文主要介绍了如何建立并应用二叉树的数据结构,遍历解析XML表示的函数计算表达式。 在遍历的过程中,读者可以根据实际的需要加入相关的处理逻辑。 本文涉及的用以演示的处理逻辑是函数计算表达式的转换,目的是把这种函数计算表达式转换为IBM®LotusForms产品所支持的函数计算表达式(即: XForms的函数计算表达式)。 如果处理逻辑非常复杂,比如,需要结合父结点来处理子结点的逻辑。 建议在清单3所示的内存模型中加入一个指向父结点的句柄(线索二叉树)。 除此之外,本文所说的方法还可以应用到更多的应用场景,在此不一一叙述。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 应用二叉树解析 XML 表示的函数计算表达式 应用 二叉 解析 表示 函数 计算 表达式
![提示](https://static.bdocx.com/images/bang_tan.gif)